代码演示
如何引入
import { Upload } from '@fibrous/semi-ui-react';
基本
最基本的用法,在触发区内放置一个 Button,点击它激活文件选择框,选择完成后自动开始上传
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return (
<Upload action="https://api.semi.design/upload">
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
</Upload>
);
}文件名超长省略
通过 showTooltip 属性,自定义设置文件名弹出提示
当类型为 boolean 时,控制是否弹出提示
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const action = 'https://api.semi.design/upload';
const button = (
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
);
return (
<>
<Upload action={action} showTooltip={false}>
{button}
</Upload>
</>
);
}当类型为 object 时,可以自定义弹出样式
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, Tooltip } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const action = 'https://api.semi.design/upload';
const button = (
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
);
return (
<>
<Upload
action={action}
showTooltip={{
renderTooltip: (content, children) => {
return (
<Tooltip content={content} position="bottom">
{children}
</Tooltip>
);
},
}}
>
{button}
</Upload>
</>
);
}添加提示文本
通过 prompt 插槽,设置自定义提示文本
通过 promptPosition 设置插槽位置,可选 left、right、bottom,默认为 right
代码
import { Fragment, type CSSProperties } from 'react';
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, type PromptPositionType } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const action = 'https://api.semi.design/upload';
const getPrompt = (pos: PromptPositionType) => {
let basicStyle = {
display: 'flex',
alignItems: 'center',
color: 'var(--semi-color-text-2)',
height: 32,
};
let marginStyle: Partial<Record<PromptPositionType, CSSProperties>> = {
left: { marginRight: 10 },
right: { marginLeft: 10 },
};
let style = { ...basicStyle, ...marginStyle[pos] };
return <div style={style}>请上传资格认证材料</div>;
};
const button = (
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
);
const positions: PromptPositionType[] = ['right', 'left', 'bottom'];
return (
<>
{positions.map((pos, index) => (
<Fragment key={pos}>
{Boolean(index) && (
<div
style={{
marginBottom: 12,
marginTop: 12,
borderBottom: '1px solid var(--semi-color-border)',
}}
/>
)}
<Upload action={action} prompt={getPrompt(pos)} promptPosition={pos}>
{button}
</Upload>
</Fragment>
))}
</>
);
}当 listType 为 picture 时,promptPosition 位置的参照对象为图片墙列表整体
代码
import { Fragment, type CSSProperties } from 'react';
import { IconPlus } from '@fibrous/semi-icons-react';
import { Upload, type FileItem, type PromptPositionType } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const action = 'https://api.semi.design/upload';
const getPrompt = (pos: PromptPositionType) => {
let basicStyle = {
display: 'flex',
alignItems: 'center',
color: 'var(--semi-color-text-2)',
height: '100%',
};
let marginStyle: Partial<Record<PromptPositionType, CSSProperties>> = {
right: { marginLeft: 10 },
};
let style = { ...basicStyle, ...marginStyle[pos] };
return <div style={style}>请上传认证材料</div>;
};
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'dy.jpeg',
status: 'success',
size: '130kb',
url: '/assets/placeholder/avatar-a.svg',
},
{
uid: '5',
name: 'resso.jpeg',
status: 'wait',
percent: 50,
size: '222kb',
url: '/assets/placeholder/avatar-b.svg',
},
];
const positions: PromptPositionType[] = ['right', 'bottom'];
return (
<>
{positions.map((pos, index) => (
<Fragment key={pos}>
{Boolean(index) && (
<div
style={{
marginBottom: 12,
marginTop: 12,
borderBottom: '1px solid var(--semi-color-border)',
}}
/>
)}
<Upload action={action} prompt={getPrompt(pos)} promptPosition={pos} listType="picture" defaultFileList={defaultFileList}>
<IconPlus aria-label="上传图片" size="extra-large" />
</Upload>
</Fragment>
))}
</>
);
}点击头像触发上传
代码
import { useState } from 'react';
import { IconCamera } from '@fibrous/semi-icons-react';
import { Upload, Avatar, Toast, type UploadProps } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const [url, setUrl] = useState('/assets/placeholder/avatar-a.svg');
const onSuccess: UploadProps['onSuccess'] = () => {
Toast.success('头像更新成功');
setUrl('/assets/ttmoment.jpeg');
};
const style = {
backgroundColor: 'var(--semi-color-overlay-bg)',
height: '100%',
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'var(--semi-color-white)',
};
const hoverMask = (
<div style={style}>
<IconCamera />
</div>
);
const api = 'https://api.semi.design/upload';
let imageOnly = 'image/*';
return (
<Upload className="avatar-upload" action={api} onSuccess={onSuccess} accept={imageOnly} showUploadList={false} onError={() => Toast.error('上传失败')}>
{/* the avatar is the whole content of the upload button, so its alt names the button */}
<Avatar src={url} alt="上传头像" style={{ margin: 4 }} hoverMask={hoverMask} />
</Upload>
);
}.avatar-upload .semi-upload-add {
border-radius: 50%; // 确保只有圆是点击热区
}
自定义上传属性
通过设置 data、headers 可添加自定义上传属性
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
// A data function is evaluated per upload, so time is the upload moment
let data = () => ({
role: 'ies',
time: new Date().getTime(),
});
let headers = {
'x-tt-semi': 'semi-upload',
};
return (
<Upload action={action} data={data} headers={headers}>
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
</Upload>
);
}上传文件类型
通过 accept 属性(input 的原生 html 属性)可以限制上传的文件类型。
accept 支持传入以下两种类型字符串:
- 文件后缀名集合(推荐),如 .jpg、.png 等;
- 文件类型的 MIME types 集合,可参考MDN 文档
例如只允许用户上传 PNG 和 PDF 文件,accept 可以这样写: accept = '.pdf,.png' 或 accept = 'application/pdf,image/png'(将 PNG 与 PDF 的 MIME type 通过,连接起来即可)。
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
let imageOnly = 'image/*';
let videoOnly = 'video/*';
let fileLimit = '.pdf,.png,.jpeg';
return (
<>
<Upload action={action} accept={imageOnly} style={{ marginBottom: 12 }}>
<Button icon={<IconUpload />} theme="light">
上传图片
</Button>
</Upload>
<Upload action={action} accept={videoOnly} style={{ marginBottom: 12 }}>
<Button icon={<IconUpload />} theme="light">
上传视频
</Button>
</Upload>
<Upload action={action} accept={fileLimit}>
<Button icon={<IconUpload />} theme="light">
上传 PDF, PNG, JPEG
</Button>
</Upload>
</>
);
}上传文件夹
通过传入 directory 为 true,可以支持上传文件夹下的所有文件
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
return (
<>
<Upload action={action} directory>
<Button icon={<IconUpload />} theme="light">
上传文件夹
</Button>
</Upload>
</>
);
}一次选中多个文件
通过设置 multiple 属性可以支持同时选中多个文件上传。
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
return (
<Upload action={action} multiple>
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
</Upload>
);
}限制文件总数量
通过设置 limit 属性可以限制最大可上传的文件数
当 limit 为1时,始终用最新上传的代替当前,并不会触发onExceed回调
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, type UploadOnChangeProps } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
let limit = 1;
let onChange = (props: UploadOnChangeProps) => {
console.log(props.fileList);
};
return (
<Upload action={action} limit={limit} onChange={onChange}>
<Button icon={<IconUpload />} theme="light">
点击上传(最多{limit}项)
</Button>
</Upload>
);
}达到上限时禁用触发控件而不是 Upload,这样仍能移除已上传的文件。
代码
import { useState } from 'react';
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, Toast, type UploadOnChangeProps } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
let [disabled, setDisabled] = useState(false);
let limit = 2;
let onChange = (props: UploadOnChangeProps) => {
let length = props.fileList.length;
if (length === limit) {
setDisabled(true);
} else {
setDisabled(false);
}
};
return (
<Upload action={action} limit={limit} onExceed={() => Toast.warning(`最多只允许上传${limit}个文件`)} onChange={onChange}>
<Button icon={<IconUpload />} theme="light" disabled={disabled}>
点击上传(最多{limit}项)
</Button>
</Upload>
);
}照片墙模式下,当已上传文件数量等于 limit 时,会自动隐藏上传入口


代码
import { IconPlus } from '@fibrous/semi-icons-react';
import { Upload, Toast, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'dyBag.png',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/root-web-sites/edit-bag.jpeg',
},
{
uid: '2',
name: 'dyBag2.jpeg',
status: 'success',
size: '222KB',
preview: true,
fileInstance: new File([new ArrayBuffer(2048)], 'dyBag2.jpeg', { type: 'image/png' }),
url: '/assets/root-web-sites/edit-bag.jpeg',
},
];
return (
<Upload action={action} limit={2} listType="picture" accept="image/*" defaultFileList={defaultFileList} onExceed={() => Toast.warning('最多只允许上传2个文件')}>
<IconPlus aria-label="上传图片" size="extra-large" />
</Upload>
);
}限制上传文件大小
通过 maxSize 和 minSize 属性可以自定义上传文件大小的限制,通过设置 onSizeError 可以设置超出限制时的回调。
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, Toast } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
return (
<>
<Upload action={action} maxSize={1024} minSize={200} onSizeError={(file) => Toast.error(`${file.name} size invalid`)}>
<Button icon={<IconUpload />} theme="light">
点击上传(最小 200KB,最大 1MB)
</Button>
</Upload>
</>
);
}自定义列表操作区
listType 为 list 时,可以通过传入 renderFileOperation 来自定义列表操作区

代码
import { IconUpload, IconDownload, IconEyeOpened, IconDelete } from '@fibrous/semi-icons-react';
import { Upload, Button, type FileItem, type RenderFileItemProps } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'dyBag.png',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/root-web-sites/edit-bag.jpeg',
},
];
const renderFileOperation = (fileItem: RenderFileItemProps) => (
<div style={{ display: 'flex', columnGap: 8, padding: '0 8px' }}>
<Button aria-label="预览" icon={<IconEyeOpened />} type="tertiary" theme="borderless" size="small" />
<Button aria-label="下载" icon={<IconDownload />} type="tertiary" theme="borderless" size="small" />
<Button onClick={() => fileItem.onRemove?.()} aria-label="删除" icon={<IconDelete />} type="tertiary" theme="borderless" size="small" />
</div>
);
return (
<Upload action={action} defaultFileList={defaultFileList} itemStyle={{ width: 300 }} renderFileOperation={renderFileOperation}>
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
</Upload>
);
}自定义文件列表标题
listType 为 list 时,可以通过 fileListTitle 自定义文件列表顶部的标题区域。支持两种形式:
字符串或 ReactNode 形式
当传入字符串或 ReactNode 时,仅替换标题文字,清空按钮保持默认样式:
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'document.pdf',
status: 'success',
size: '130KB',
},
{
uid: '2',
name: 'report.xlsx',
status: 'success',
size: '222KB',
},
];
return (
<>
<Upload action={action} defaultFileList={defaultFileList} fileListTitle="已上传文件" style={{ marginBottom: 20 }}>
<Button icon={<IconUpload />} theme="light">
自定义标题文字
</Button>
</Upload>
<Upload action={action} defaultFileList={defaultFileList} fileListTitle={<span style={{ color: 'var(--semi-color-primary)', fontWeight: 600 }}>📁 重要文件</span>}>
<Button icon={<IconUpload />} theme="light">
带样式的标题
</Button>
</Upload>
</>
);
}函数形式
当传入函数时,可以完全自定义标题区域,包括清空按钮。函数会接收以下参数:
fileList: 当前文件列表onClear: 清空文件的回调函数clearText: 清空按钮的默认文案(根据当前语言环境)
代码
import { IconUpload, IconClose } from '@fibrous/semi-icons-react';
import { Upload, Button, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'document.pdf',
status: 'success',
size: '130KB',
},
{
uid: '2',
name: 'report.xlsx',
status: 'success',
size: '222KB',
},
];
return (
<>
<Upload
action={action}
defaultFileList={defaultFileList}
style={{ marginBottom: 20 }}
fileListTitle={({ fileList, onClear, clearText }) => (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
}}
>
<span style={{ color: 'var(--semi-color-primary)' }}>📂 共 {fileList.length} 个文件</span>
<Button size="small" type="danger" icon={<IconClose />} onClick={onClear}>
{clearText}
</Button>
</div>
)}
>
<Button icon={<IconUpload />} theme="light">
自定义清空按钮样式
</Button>
</Upload>
<Upload
action={action}
defaultFileList={defaultFileList}
fileListTitle={({ fileList, onClear }) => (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
}}
>
<span>
已选择 <strong style={{ color: 'var(--semi-color-danger)' }}>{fileList.length}</strong> 个文件
</span>
<button
type="button"
onClick={onClear}
style={{
padding: 0,
border: 'none',
background: 'none',
font: 'inherit',
cursor: 'pointer',
color: 'var(--semi-color-link)',
fontSize: 12,
}}
>
全部移除
</button>
</div>
)}
>
<Button icon={<IconUpload />} theme="light">
完全自定义标题区域
</Button>
</Upload>
</>
);
}自定义预览逻辑
listType 为 list 时,可以通过传入 previewFile 自定义预览逻辑
例如你不需要对图片类型进行缩略图预览时,可以在 previewFile 中恒定返回一个<IconFile />
假如你希望点击图片时放大预览,则可以在 previewFile中使用 Image 组件
或者你希望使用额外的操作区来实现点击放大预览,你也可以结合 renderFileOperation 放置一些自定义元素例如 Icon 图标实现点击放大

代码
import { IconUpload, IconFile } from '@fibrous/semi-icons-react';
import { Upload, Button, Image, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'dyBag.png',
status: 'success',
size: '130KB',
url: '/assets/root-web-sites/edit-bag.jpeg',
},
{
uid: '2',
name: 'dyBag2.png',
status: 'success',
size: '130KB',
url: '/assets/root-web-sites/edit-bag.jpeg',
},
];
return (
<Upload defaultFileList={defaultFileList} action={action} previewFile={(file) => (file.uid === '1' ? <IconFile size="large" /> : <Image src={file.url} alt="" />)}>
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
</Upload>
);
}结合 renderFileOperation 与 ImagePreview 的示例,以下示例点击右侧第一个 Icon 可放大图片预览

代码
import { useState } from 'react';
import { IconUpload, IconDownload, IconDelete, IconExpand } from '@fibrous/semi-icons-react';
import { Upload, Button, ImagePreview, type FileItem, type RenderFileItemProps } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const [visible, setVisible] = useState(false);
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'dyBag.png',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/root-web-sites/edit-bag.jpeg',
},
];
const renderFileOperation = (fileItem: RenderFileItemProps) => (
<div style={{ display: 'flex', columnGap: 8, padding: '0 8px' }}>
<Button aria-label="预览" icon={<IconExpand />} type="tertiary" theme="borderless" size="small" onClick={() => setVisible(true)} />
<Button aria-label="下载" icon={<IconDownload />} type="tertiary" theme="borderless" size="small" />
<Button aria-label="删除" onClick={() => fileItem.onRemove?.()} icon={<IconDelete />} type="tertiary" theme="borderless" size="small" />
<ImagePreview src={fileItem.url} visible={visible} onVisibleChange={setVisible} />
</div>
);
return (
<Upload action={action} defaultFileList={defaultFileList} itemStyle={{ width: 300 }} renderFileOperation={renderFileOperation}>
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
</Upload>
);
}默认文件列表
通过 defaultFileList 可以展示已上传的文件。当需要预览默认文件的缩略图时,你可以将 defaultFileList 内对应 item 的 preview 属性设为 true


代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'dyBag.jpeg',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/root-web-sites/edit-bag.jpeg',
},
{
uid: '2',
name: 'dyBag2.jpeg',
status: 'uploadFail',
size: '222KB',
preview: true,
fileInstance: new File([new ArrayBuffer(2048)], 'dyBag2.jpeg', { type: 'image/png' }),
url: '/assets/root-web-sites/edit-bag.jpeg',
},
];
return (
<>
<Upload action={action} defaultFileList={defaultFileList}>
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
</Upload>
</>
);
}受控组件
当传入fileList时,作为受控组件使用。需要监听 onChange 回调,并且将 fileList 回传给 Upload(注意需传入一个新的数组对象)

代码
import { useState } from 'react';
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, type FileItem, type UploadOnChangeProps } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const initList: FileItem[] = [
{
uid: '1',
name: 'dyBag.jpeg',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/root-web-sites/edit-bag.jpeg',
},
{
uid: '2',
name: 'dy.jpeg',
status: 'uploading',
size: '222KB',
percent: 50,
preview: true,
fileInstance: new File([new ArrayBuffer(2048)], 'dy.jpeg', { type: 'image/jpeg' }),
url: '/assets/placeholder/avatar-a.svg',
},
];
const [list, updateList] = useState(initList);
const onChange = ({ fileList, currentFile }: UploadOnChangeProps) => {
console.log('onChange');
console.log(fileList);
console.log(currentFile);
let newFileList = [...fileList]; // 展开得到新数组
updateList(newFileList);
};
return (
<Upload action="https://api.semi.design/upload" onChange={onChange} fileList={list} showRetry={false}>
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
</Upload>
);
}图片墙
设置 listType = 'picture',用户可以上传图片并在列表中显示缩略图
如果通过 defaultFileList 或 fileList 设置已上传的文件列表时,会自动读取对象数组中的 url 属性用于展示图片
代码
import { IconPlus } from '@fibrous/semi-icons-react';
import { Upload, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'dy.png',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/placeholder/avatar-a.svg',
},
];
return (
<>
<Upload action={action} listType="picture" accept="image/*" multiple defaultFileList={defaultFileList}>
<IconPlus aria-label="上传图片" size="extra-large" />
</Upload>
</>
);
}设置 showPicInfo,可以查看图片基础信息
代码
import { IconPlus } from '@fibrous/semi-icons-react';
import { Upload, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'music.png',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/placeholder/avatar-b.svg',
},
{
uid: '2',
name: 'brand.png',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/placeholder/avatar-b.svg',
},
];
return (
<>
<Upload action={action} listType="picture" showPicInfo accept="image/*" multiple defaultFileList={defaultFileList}>
<IconPlus aria-label="上传图片" size="extra-large" />
</Upload>
</>
);
}图片墙放大预览
配合 Image 组件,通过 renderThumbnail API ,可以实现点击图片放大预览
代码
import { IconPlus } from '@fibrous/semi-icons-react';
import { Upload, Image, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'music.png',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/placeholder/avatar-b.svg',
},
];
return (
<>
<Upload action={action} listType="picture" accept="image/*" multiple defaultFileList={defaultFileList} renderThumbnail={(file) => <Image src={file.url} alt={file.name} />}>
<IconPlus aria-label="Upload picture" size="extra-large" />
</Upload>
</>
);
}可以通过 renderPicPreviewIcon,onPreviewClick 来自定义预览图标,当显示替换图标 showReplace 时,不会再显示预览图标
当需要自定义预览/替换功能时,需要关闭替换功能,使用 renderPicPreviewIcon 监听图标点击事件即可。
onPreviewClick 监听的是单张图片容器的点击事件
代码
import { IconPlus, IconEyeOpened } from '@fibrous/semi-icons-react';
import { Upload, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'resso.png',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/placeholder/avatar-b.svg',
},
];
const handlePreview = (file: FileItem) => {
const feature = 'width=300,height=300';
window.open(file.url, 'imagePreview', feature);
};
return (
<>
<Upload action={action} listType="picture" showPicInfo accept="image/*" multiple defaultFileList={defaultFileList} onPreviewClick={handlePreview} renderPicPreviewIcon={() => <IconEyeOpened style={{ color: 'var(--semi-color-white)', fontSize: 24 }} />}>
<IconPlus aria-label="上传图片" size="extra-large" />
</Upload>
</>
);
}图片墙设置宽高
通过设置 picHeight, picWidth,可以统一设置图片墙元素的宽高。如果同时使用 renderThumbnail return Image 组件来实现点击放大预览,你需要同时指定 Image 组件的 width 和 height

代码
import { IconPlus } from '@fibrous/semi-icons-react';
import { Upload, Image, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'image-1.jpg',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/root-web-sites/abstract.jpg',
},
];
return (
<>
<Upload action={action} listType="picture" accept="image/*" multiple defaultFileList={defaultFileList} picHeight={110} picWidth={200} renderThumbnail={(file) => <Image src={file.url} alt={file.name} width={200} height={110} />}>
<IconPlus aria-label="上传图片" size="extra-large" style={{ margin: 4 }} />
点击添加图片
</Upload>
</>
);
}设置 hotSpotLocation 自定义点击热区的顺序,默认在照片墙列表结尾
代码
import { useState } from 'react';
import { IconPlus } from '@fibrous/semi-icons-react';
import { Upload, RadioGroup, Radio, type FileItem, type UploadProps } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
let action = 'https://api.semi.design/upload';
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'resso.png',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/placeholder/avatar-b.svg',
},
];
const handlePreview = (file: FileItem) => {
const feature = 'width=300,height=300';
window.open(file.url, 'imagePreview', feature);
};
const [hotSpotLocation, setLocation] = useState<UploadProps['hotSpotLocation']>('end');
return (
<>
<RadioGroup value={hotSpotLocation} type="button" onChange={(e) => setLocation(e.target.value)}>
<Radio value="start">start</Radio>
<Radio value="end">end</Radio>
</RadioGroup>
<hr />
<Upload action={action} listType="picture" showPicInfo accept="image/*" multiple hotSpotLocation={hotSpotLocation} defaultFileList={defaultFileList} onPreviewClick={handlePreview}>
<IconPlus aria-label="上传图片" size="extra-large" />
</Upload>
</>
);
}禁用
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, type FileItem } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const defaultFileList: FileItem[] = [
{
uid: '1',
name: 'first.png',
status: 'success',
size: '130KB',
preview: true,
url: '/assets/placeholder/avatar-a.svg',
},
{
uid: '2',
name: 'second.png',
status: 'validateFail',
size: '222KB',
preview: true,
url: '/assets/placeholder/avatar-a.svg',
},
];
let action = 'https://api.semi.design/upload';
return (
<>
<Upload action={action} disabled defaultFileList={defaultFileList}>
<Button icon={<IconUpload />} theme="light" disabled>
点击上传
</Button>
</Upload>
</>
);
}手动触发上传
uploadTrigger='custom',选中文件后将不会自动触发上传。需要手动调用 ref 上的 upload 方法触发
代码
import { useRef } from 'react';
import { IconUpload, IconPlus } from '@fibrous/semi-icons-react';
import { Upload, Button, type UploadHandle } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const uploadRef = useRef<UploadHandle>(null);
const manulUpload = () => {
uploadRef.current?.upload();
};
let action = 'https://api.semi.design/upload';
return (
<div>
<Upload accept="image/gif, image/png, image/jpeg, image/bmp, image/webp" action={action} uploadTrigger="custom" ref={uploadRef} onSuccess={(...v) => console.log(...v)} onError={(...v) => console.log(...v)}>
<Button icon={<IconPlus />} theme="light" style={{ marginRight: 8 }}>
选择文件
</Button>
</Upload>
<Button icon={<IconUpload />} theme="light" onClick={manulUpload}>
开始上传
</Button>
</div>
);
}拖拽上传
draggable='true',可以使用拖拽功能
代码
import { Upload } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return <Upload action="https://api.semi.design/upload" draggable dragMainText="点击上传文件或拖拽文件到这里" dragSubText="支持任意类型文件" />;
}可以通过 dragIcon、dragMainText、dragSubText 快捷设置拖拽区内容
代码
import { IconBolt } from '@fibrous/semi-icons-react';
import { Upload } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return <Upload action="https://api.semi.design/upload" dragIcon={<IconBolt />} draggable accept="application/pdf,.jpeg" dragMainText="点击上传文件或拖拽文件到这里" dragSubText="仅支持jpeg、pdf" style={{ marginTop: 10 }} />;
}还可以通过 children 传入 ReactNode,完全自定义拖拽区的显示
代码
import { IconBolt } from '@fibrous/semi-icons-react';
import { Upload } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return (
<Upload action="https://api.semi.design/upload" dragIcon={<IconBolt />} draggable accept="application/pdf,.jpeg" style={{ marginTop: 10 }}>
<div className="components-upload-demo-drag-area">
<img src="/assets/placeholder/avatar-a.svg" height="96" alt="demo img" style={{ borderRadius: 4 }} />
<div
style={{
fontSize: 14,
marginTop: 8,
flexBasis: '100%',
textAlign: 'center',
color: 'var(--semi-color-tertiary)',
}}
>
Wow, you can really dance.
</div>
</div>
</Upload>
);
}Scss 样式如下
.components-upload-demo-drag-area {
border-radius: var(--semi-border-radius-small);
border: 2px dashed var(--semi-color-border);
width: 100%;
padding: 12px;
background-color: var(--semi-color-tertiary-light-default);
display: flex;
cursor: pointer;
flex-wrap: wrap;
justify-content: center;
&:hover {
background-color: var(--semi-color-primary-light-default);
border-color: var(--semi-color-primary);
}
}
粘贴上传
设置 addOnPasting 后,在页面任意位置粘贴(paste 事件)的文件会加入文件列表,不需要剪贴板授权。读取粘贴的文件失败时触发 onPastingError。
代码
import { Upload, Toast } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return <Upload action="https://api.semi.design/upload" addOnPasting draggable dragMainText="复制截图或文件后,在页面上按 Ctrl/Cmd + V" dragSubText="也可以点击或拖拽上传" onPastingError={() => Toast.error('读取剪贴板中的文件失败')} />;
}上传前自定义校验
可通过 beforeUpload 钩子,对文件状态进行更新,这是在网络上传前,选择文件后进行校验,({ file: FileItem, fileList: Array<FileItem> }) => beforeUploadResult | Promise | boolean 同步校验时需返回 boolean(true 为校验通过,false 为校验失败,校验失败会阻止文件网络上传)或者一个 Object 对象,具体结构如下
// beforeUploadResult:
{
fileInstance?: File,
status?: 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait',
validateMessage?: React.ReactNode | string, // 文件的校验信息
shouldUpload: boolean, // 是否需要上传。默认为true,如果为false,该fileItem只会被展示在列表中,不会触发上传操作
autoRemove: boolean, // 是否从fileList中移除该文件,默认为false
}
代码
import { useRef } from 'react';
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, type BeforeUploadProps, type BeforeUploadObjectResult } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const countRef = useRef(0);
const transformFile = (fileInstance: File) => {
if (countRef.current === 0) {
return new File([fileInstance], 'newFileName', { type: 'image/png' });
}
return fileInstance;
};
const beforeUpload = ({ file }: BeforeUploadProps) => {
let result: BeforeUploadObjectResult;
if (countRef.current > 0) {
result = {
autoRemove: false,
fileInstance: file.fileInstance,
shouldUpload: true,
};
} else {
result = {
autoRemove: false,
fileInstance: file.fileInstance,
status: 'validateFail',
shouldUpload: false,
};
}
countRef.current = countRef.current + 1;
return result;
};
return (
<Upload action="https://api.semi.design/upload" transformFile={transformFile} beforeUpload={beforeUpload}>
<Button icon={<IconUpload />} theme="light">
点击上传(上传前同步校验)
</Button>
</Upload>
);
}异步校验时,需返回 Promise,Promise resolve 代表检验通过,reject 代表校验失败,不会触发上传。
resolve/reject 时可以传入 object(结构同上 beforeUploadResult)
代码
import { useRef } from 'react';
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, type BeforeUploadProps, type BeforeUploadObjectResult } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const countRef = useRef(0);
const beforeUpload = ({ file }: BeforeUploadProps) => {
return new Promise<BeforeUploadObjectResult>((resolve, reject) => {
if (countRef.current > 1) {
const result = {
autoRemove: false,
shouldUpload: true,
};
countRef.current = countRef.current + 1;
resolve(result);
} else {
const result = {
autoRemove: false,
fileInstance: file.fileInstance,
status: 'validateFail',
shouldUpload: false,
validateMessage: `第${countRef.current + 1}个注定失败`,
};
countRef.current = countRef.current + 1;
reject(result);
}
});
};
return (
<Upload action="https://api.semi.design/upload" beforeUpload={beforeUpload}>
<Button icon={<IconUpload />} theme="light">
点击上传(上传前异步校验)
</Button>
</Upload>
);
}上传后更新文件信息
可以通过 afterUpload 钩子,对文件状态,校验信息,文件名进行更新。
({ response: any, file: FileItem, fileList: Array<FileItem> }) => afterUploadResult
afterUpload 在上传完成后(xhr.onload)且没有发生错误的情况下触发,需返回一个 Object 对象(不支持异步返回),具体结构如下
// afterUploadResult:
{
status?: 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait';
// 文件的校验信息
validateMessage?: React.ReactNode | string;
// 是否从fileList中移除该文件,默认为false
autoRemove?: boolean;
// 文件的名称
name?: string;
// 预览文件的url,一般为当次上传请求中 Server 接收到文件后返回的存储地址
url?: string
}
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, type AfterUploadProps } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const afterUpload = ({ response }: AfterUploadProps) => {
if (response.status_code === 200) {
return {
autoRemove: false,
status: 'uploadFail',
validateMessage: '内容不合法',
name: 'RenameByServer.jpg',
url: '/assets/root-web-sites/edit-bag.jpeg',
};
} else {
return {};
}
};
return (
<Upload action="https://api.semi.design/upload" afterUpload={afterUpload}>
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
</Upload>
);
}自定义请求
当传入 customRequest 时, 相当于使用的自定义的请求方法替换了 upload 内置的 xhr 请求,用户需要自行接管上传行为。
可在入参中获取到当前操作的 file 对象,用户自行实现上传过程,并且在适当的时候调用 customRequest 入参中的 onProgress、onError、onSuccess 以更新 Upload 组件内部状态进而驱动 UI 更新
customRequest 包含以下入参
{
// 当前文件名称
fileName: string,
// 用户设置的props.data
data: object,
// FileItem,具体结构参考下面的文档
file: FileItem,
// original File Object which extends Blob, 浏览器实际获取到的文件对象(https://developer.mozilla.org/zh-CN/docs/Web/API/File)
fileInstance: File,
// 上传过程中应调用的函数,event需要包含 total、loaded属性
onProgress: (event: { total: number, loaded: number }) => any,
// 上传出错时应调用的函数
onError: (userXhr: { status?: number }, e?: Event) => any,
// 上传成功后应调用的函数, response为上传成功后的请求结果
onSuccess: (response: any, e?: Event) => any,
// 用户设置的props.withCredentials
withCredentials: boolean,
// 用户设置的props.action
action: string,
}
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button, type UploadCustomRequestArgs } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
const mockRequest = ({ onProgress, onSuccess }: UploadCustomRequestArgs) => {
let count = 0;
let interval = setInterval(() => {
if (count === 100) {
clearInterval(interval);
onSuccess({});
return;
}
onProgress({ total: 100, loaded: count });
count += 20;
}, 500);
};
return (
<Upload action="https://api.semi.design/upload" customRequest={mockRequest}>
<Button icon={<IconUpload />} theme="light">
点击上传
</Button>
</Upload>
);
}图片裁切
通过 crop 属性启用图片裁切功能。支持点击选择、拖拽、粘贴、替换文件时进行裁切。
基本用法
设置 crop={true} 启用默认裁切配置。
裁切前会先按 accept 与 limit 过滤,被拒绝或超出数量的图片不会进入裁切。浏览器只能编码 PNG、JPEG、WebP,其他格式(GIF、BMP、SVG)的裁切结果为 PNG,文件的 type 与扩展名随之变为 image/png 与 .png。
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return (
<Upload
action="https://api.semi.design/upload"
crop
onSuccess={(response, file) => {
console.log('上传成功:', response, file);
}}
>
<Button icon={<IconUpload />} theme="light">
点击上传(启用裁切)
</Button>
</Upload>
);
}自定义裁切配置
通过对象形式配置裁切参数,包括宽高比、形状、质量等。
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return (
<Upload
action="https://api.semi.design/upload"
crop={{
aspectRatio: 16 / 9,
shape: 'rect',
quality: 0.8,
modalTitle: '裁切图片',
modalOkText: '确认裁切',
modalCancelText: '取消',
}}
onSuccess={(response, file) => {
console.log('上传成功:', response, file);
}}
>
<Button icon={<IconUpload />} theme="light">
点击上传(16:9 裁切)
</Button>
</Upload>
);
}圆形裁切
适用于头像上传场景,设置 shape: 'round' 启用圆形裁切。
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return (
<Upload
action="https://api.semi.design/upload"
crop={{
aspectRatio: 1,
shape: 'round',
quality: 0.9,
modalTitle: '裁切头像',
}}
onSuccess={(response, file) => {
console.log('上传成功:', response, file);
}}
>
<Button icon={<IconUpload />} theme="light">
点击上传(圆形裁切)
</Button>
</Upload>
);
}裁切前确认
通过 beforeCrop 回调,在裁切前进行确认或其他处理。返回 false 可跳过裁切直接上传。
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return (
<Upload
action="https://api.semi.design/upload"
crop={{
aspectRatio: 1,
shape: 'round',
}}
beforeCrop={(file, fileList) => {
console.log('beforeCrop:', file, fileList);
// true continues cropping; false skips it and uploads directly
return window.confirm('是否裁切图片?');
}}
onCropError={(error) => {
console.error('裁切失败:', error);
}}
onSuccess={(response, file) => {
console.log('上传成功:', response, file);
}}
>
<Button icon={<IconUpload />} theme="light">
点击上传(裁切前确认)
</Button>
</Upload>
);
}拖拽上传裁切
拖拽上传时也会触发裁切功能。
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return (
<Upload
action="https://api.semi.design/upload"
crop={{
aspectRatio: 1,
shape: 'rect',
}}
draggable
onDrop={(_e, files) => {
console.log('拖拽文件:', files);
}}
onSuccess={(response, file) => {
console.log('上传成功:', response, file);
}}
>
<Button icon={<IconUpload />} theme="light">
拖拽上传(启用裁切)
</Button>
</Upload>
);
}自定义裁切弹窗样式
通过 cropModalProps 自定义裁切弹窗的样式和属性。弹窗的 width 缺省为 600,传入的值优先。
cropModalProps 的类型为 UploadCropModalProps,不含 visible、title、okText、cancelText、onOk、onCancel:标题和按钮文字用 crop 的 modalTitle、modalOkText、modalCancelText 配置,其余由组件内置。
代码
import { IconUpload } from '@fibrous/semi-icons-react';
import { Upload, Button } from '@fibrous/semi-ui-react';
export default function UploadDemo() {
return (
<Upload
action="https://api.semi.design/upload"
crop={{
aspectRatio: 1,
shape: 'rect',
}}
cropModalProps={{
width: 800,
style: { height: 600 },
bodyStyle: { height: 500, backgroundColor: '#f5f5f5' },
}}
onSuccess={(response, file) => {
console.log('上传成功:', response, file);
}}
>
<Button icon={<IconUpload />} theme="light">
自定义弹窗样式
</Button>
</Upload>
);
}API 参考
Upload
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| accept | 接受上传的文件类型,写法同 input 的 accept:文件后缀(如 .jpg)或 MIME type,用逗号分隔。 | string | - |
| action | 文件上传地址,必填。 | string | - |
| addOnPasting | 粘贴(paste 事件)时是否把剪贴板中的文件加入文件列表,无需额外授权。 | boolean | - |
| afterUpload | 上传完成且没有出错后的钩子,返回的对象用于更新文件状态、校验信息、名称和预览地址;不支持异步返回。 | Type(object: AfterUploadProps) => AfterUploadResult | - |
| beforeClear | 清空文件前的回调,返回 false、resolve 为 false 的 Promise 或 reject 的 Promise 会阻止清空。 | Type(fileList: Array<FileItem>) => boolean | Promise<boolean> | Type() => true |
| beforeCrop | 图片裁切前的回调,只用第一张选中的图片调用一次,结果决定整批是否裁切。返回 false 跳过裁切直接上传,支持异步返回。 | Type(file: File, fileList: File[]) => boolean | Promise<boolean> | - |
| beforeRemove | 移除文件前的回调,返回 false、resolve 为 false 的 Promise 或 reject 的 Promise 会阻止移除。 | Type(file: FileItem, fileList: Array<FileItem>) => boolean | Promise<boolean> | Type() => true |
| beforeUpload | 上传文件前的钩子,返回的对象用于更新文件状态并控制是否上传;返回 boolean 表示通过或失败,返回 Promise 时 resolve 为通过、reject 为失败。 | Type(object: BeforeUploadProps) => BeforeUploadObjectResult | Promise<BeforeUploadObjectResult> | boolean | - |
| capture | 媒体拍摄的方式,可选 user、environment;传 true 输出空值属性,传 false 不输出该属性。 | boolean | 'user' | 'environment' | - |
| children | 触发区内容。拖拽模式下替换整个默认拖拽区内容。 | ReactNode | - |
| className | 根元素的类名。 | string | - |
| crop | 启用图片裁切。传 true 使用默认配置,传对象自定义裁切参数;点击选择、拖拽、粘贴、替换都会裁切。 | boolean | CropProps | - |
| cropModalProps | 裁切弹窗 Modal 的属性;visible、title、okText、cancelText、onOk、onCancel 由组件内置,width 缺省为 600。 | UploadCropModalProps | - |
| customRequest | 替换内置 XMLHttpRequest 的自定义请求方法,需自行调用入参的 onProgress、onError、onSuccess 更新状态。 | Type(object: customRequestArgs) => void | - |
| data | 上传时附带的额外参数,或根据文件返回额外参数的函数。 | TypeRecord<string, any> | ((file: File) => Record<string, unknown>) | - |
| defaultFileList | 初始的已上传文件列表,非受控。每项的 uid 必填且不能重复。 | TypeArray<FileItem> | [] |
| directory | 是否上传文件夹。 | boolean | - |
| disabled | 是否禁用。禁用后不再显示移除、重试、替换和清空操作。 | boolean | false |
| draggable | 是否支持拖拽上传。 | boolean | - |
| dragIcon | 拖拽区左侧的图标,不传时显示默认图标。 | ReactNode | - |
| dragMainText | 拖拽区的主文本,不传时显示语言包中的文案。 | ReactNode | - |
| dragSubText | 拖拽区的帮助文本。 | ReactNode | - |
| fileList | 受控的文件列表。传入后 Upload 即为受控组件,需在 onChange 中回写新数组。每项的 uid 必填且不能重复。 | TypeArray<FileItem> | - |
| fileListTitle | 文件列表的标题区域。传入 ReactNode 时只替换标题文字,清空按钮保持默认;传入函数时可完全自定义标题区(含清空按钮),参数为 fileList、onClear、clearText。 | TypeReactNode | ((props: RenderFileListTitleProps) => ReactNode) | - |
| fileName | 作用与 name 相同,主要在 Form.Upload 中使用,避免与 Field 的 name 冲突。 | string | - |
| headers | 上传时附带的 headers,或根据文件返回 headers 的函数。 | TypeRecord<string, any> | ((file: File) => Record<string, string>) | - |
| hotSpotLocation | 照片墙添加按钮的位置,可选 start 或 end。 | 'start' | 'end' | "end" |
| itemStyle | 文件卡片的内联样式。 | CSSProperties | - |
| limit | 最多允许上传的文件个数。为 1 时始终用最新选择的文件替换当前文件,不触发 onExceed。 | number | - |
| listType | 文件列表的展示类型,可选 list、picture、none;none 不渲染文件列表,只保留触发区。 | UploadListType | "list" |
| maxSize | 文件体积上限,单位 KB。 | number | - |
| minSize | 文件体积下限,单位 KB。 | number | - |
| multiple | 是否允许一次选择多个文件。 | boolean | false |
| name | 上传时使用的文件字段名。 | string | - |
| onAcceptInvalid | 接收到的文件不符合 accept 时的回调,参数为不符合的文件(一般是选择文件夹或拖入了格式不符的文件)。 | Type(files: File[]) => void | - |
| onChange | 文件状态变化时调用,包括上传中、成功、失败。参数为 { fileList, currentFile },通过 insert 插入文件时 currentFile 为 null。 | Type(object: OnChangeProps) => void | - |
| onClear | 点击清空时的回调。 | Type() => void | - |
| onCropError | 图片裁切失败时的回调,参数为错误对象。 | Type(error: Error) => void | - |
| onDrop | 拖拽的元素在拖拽区上释放时调用,参数为原生 drop 事件、拖入的文件和当前文件列表。 | Type(e: globalThis.DragEvent, files: Array<File>, fileList: Array<FileItem>) => void | - |
| onError | 上传出错时的回调,参数为错误、文件、当前文件列表和 xhr。使用 customRequest 时,xhr 是使用方传给 customRequest 的 onError 的对象。 | Type(e: CustomError, file: CustomFile, fileList: Array<FileItem>, xhr: XMLHttpRequest | { status?: number }) => void | - |
| onExceed | 文件总数超出 limit 时的回调,参数为截断前本次选中的原始文件。 | Type(fileList: Array<File>) => void | - |
| onFileChange | 选中文件后的回调,参数为选中的文件。 | Type(files: Array<File>) => void | - |
| onOpenFileDialog | 打开系统文件选择窗口时的回调。 | Type() => void | - |
| onPastingError | addOnPasting 为 true 时,读取剪贴板中的文件失败(getAsFile() 返回 null 或抛错)的回调。 | Type(error: Error) => void | - |
| onPreviewClick | 点击文件卡片时的回调,参数为该文件。传入后文件卡片可点击并显示预览按钮,是否传入会改变渲染。 | Type(fileItem: FileItem) => void | - |
| onProgress | 上传过程中的回调,参数为百分比、文件和当前文件列表。 | Type(percent: number, file: CustomFile, fileList: Array<FileItem>) => void | - |
| onRemove | 移除文件后的回调,参数为被移除的 File 对象、当前文件列表和被移除的文件项。来自 defaultFileList 且没有 File 对象的项,第一个参数为 undefined。 | Type(currentFile: CustomFile | undefined, fileList: Array<FileItem>, currentFileItem: FileItem) => void | - |
| onRetry | 点击重试时的回调,参数为文件。 | Type(fileItem: FileItem) => void | - |
| onSizeError | 文件体积不符合 maxSize 或 minSize 时的回调,参数为文件和当前文件列表。 | Type(file: CustomFile, fileList: Array<FileItem>) => void | - |
| onSuccess | 上传成功后的回调,参数为服务端响应、文件和当前文件列表。 | Type(responseBody: any, file: CustomFile, fileList: Array<FileItem>) => void | - |
| picHeight | 照片墙模式下图片的展示高度。 | string | number | - |
| picWidth | 照片墙模式下图片的展示宽度。 | string | number | - |
| previewFile | 自定义预览逻辑,返回的内容替换原缩略图,只在 list 模式下有效。 | Type(renderFileItemProps: RenderFileItemProps) => ReactNode | - |
| prompt | 提示文本,ReactNode。与直接写在 children 中不同,点击它不会触发上传。 | ReactNode | - |
| promptPosition | 提示文本的位置,可选 left、right、bottom。list 模式相对触发区,picture 模式相对图片列表。 | PromptPositionType | "right" |
| ref | 命令式句柄,挂载 insert、upload、openFileDialog 方法。 | TypeReact.Ref<UploadHandle> | - |
| renderFileItem | 自定义文件卡片的渲染,参数为 RenderFileItemProps,并额外包含 key。 | Type(renderFileItemProps: RenderFileItemProps & { key: string }) => ReactNode | - |
| renderFileOperation | 自定义列表项的操作区,只在 list 模式下有效。 | Type(fileItem: RenderFileItemProps) => ReactNode | - |
| renderPicClose | 自定义照片墙的关闭按钮,参数为 className 和 remove,只在照片墙模式下有效。 | Type(renderPicCloseProps: RenderPictureCloseProps) => ReactNode | - |
| renderPicInfo | 自定义照片墙信息,只在照片墙模式且 showPicInfo 为 true 时有效。 | Type(renderFileItemProps: RenderFileItemProps) => ReactNode | - |
| renderPicPreviewIcon | 自定义照片墙悬停时的预览图标,只在照片墙模式下有效。 | Type(renderFileItemProps: RenderFileItemProps) => ReactNode | - |
| renderThumbnail | 自定义照片墙缩略图,只在照片墙模式下有效。 | Type(renderFileItemProps: RenderFileItemProps) => ReactNode | - |
| showClear | list 模式下,limit 不为 1 且至少有一个文件时,是否展示清空按钮。 | boolean | true |
| showPicInfo | 是否显示图片信息,只在照片墙模式下有效。 | boolean | false |
| showReplace | 上传成功后,文件卡片内是否展示替换按钮。 | boolean | false |
| showRetry | 上传失败时,文件卡片内是否展示重试按钮。 | boolean | true |
| showTooltip | 文件名过长时是否展示提示。传对象时可配置 type(tooltip 或 popover)、透传给浮层的 opts,以及自定义弹出层的 renderTooltip。 | boolean | ShowTooltip | true |
| showUploadList | 是否显示文件列表。 | boolean | true |
| style | 根元素的样式。 | CSSProperties | - |
| timeout | 不生效:组件不读取该属性。 | number | - |
| transformFile | 选中文件后、上传前的转换函数,返回要上传的 File。 | Type(file: File) => File | - |
| uploadTrigger | 触发上传的时机,auto 选中后自动上传,custom 需调用 ref 上的 upload 方法。 | 'auto' | 'custom' | "auto" |
| validateMessage | Upload 整体的校验信息,ReactNode。 | ReactNode | - |
| validateStatus | 校验状态,影响根元素的样式,可选 default、error、warning、success。 | ValidateStatus | - |
| withCredentials | 上传请求是否携带 Cookie。 | boolean | false |
Methods
| 方法 | 说明 | 类型 |
|---|---|---|
| insert | 上传文件,传入 index 时插入到指定位置,不传则插入到最后。 | Type(files: Array<CustomFile>, index?: number) => void |
| openFileDialog | 打开文件选择窗口。 | Type() => void |
| upload | 手动开始上传,配合 uploadTrigger 为 custom 使用。 | Type() => void |
未声明的原生属性(如 aria-label、id、onClick)落在根元素上;根元素带 aria-label 或 aria-labelledby 时自动加 role="group"。
Upload.FileCard
Upload.FileCard 是默认的文件卡片,用于在 renderFileItem 中保留默认外观:<Upload.FileCard {...props} />。data-*、aria-* 落在根元素上。
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| _sizeInvalid | 内部标记,文件大小超出限制时由组件设置,Upload 据此显示大小不合法的提示,无需手动传入。 | boolean | - |
| className | 文件卡片的类名,不会应用到根元素。 | string | - |
| disabled | 是否禁用,禁用后不显示移除、重试和替换按钮。 | boolean | - |
| event | xhr 事件。 | Event | - |
| fileInstance | 浏览器实际获取到的文件对象,继承自 Blob。 | File | - |
| index | 文件在列表中的位置。 | number | - |
| listType | 卡片的展示类型,可选 list、picture、none。 | UploadListType | "list" |
| name | 文件名。 | string | "" |
| onPreviewClick | 点击卡片时的回调。传入后卡片可点击并显示预览按钮,是否传入会改变渲染。 | Type() => void | - |
| onRemove | 点击移除时的回调。 | Type() => void | - |
| onReplace | 点击替换时的回调。 | Type() => void | - |
| onRetry | 点击重试时的回调。 | Type() => void | - |
| percent | 上传进度百分比。 | number | - |
| picHeight | 照片墙模式下图片的高度。 | string | number | - |
| picWidth | 照片墙模式下图片的宽度。 | string | number | - |
| preview | 是否根据 url 进行预览。 | boolean | false |
| previewFile | 自定义预览逻辑,返回的内容替换原缩略图,只在 list 模式下有效。 | Type(fileItem: RenderFileItemProps) => ReactNode | - |
| renderFileOperation | 自定义列表项的操作区,只在 list 模式下有效。 | Type(fileItem: RenderFileItemProps) => ReactNode | - |
| renderPicClose | 自定义照片墙的关闭按钮,参数为 className 和 remove,只在照片墙模式下有效。 | Type(renderPicCloseProps: RenderPictureCloseProps) => ReactNode | - |
| renderPicInfo | 自定义照片墙信息,只在照片墙模式下有效。 | Type(renderFileItemProps: RenderFileItemProps) => ReactNode | - |
| renderPicPreviewIcon | 自定义照片墙悬停时的预览图标,只在照片墙模式下有效。 | Type(renderFileItemProps: RenderFileItemProps) => ReactNode | - |
| renderThumbnail | 自定义照片墙缩略图,只在照片墙模式下有效。 | Type(renderFileItemProps: RenderFileItemProps) => ReactNode | - |
| response | xhr 的响应。请求成功时为响应体,失败时为对应的错误。 | any | - |
| shouldUpload | 是否应该继续上传。 | boolean | - |
| showPicInfo | 是否显示图片信息,只在照片墙模式下有效。 | boolean | - |
| showReplace | 上传成功后是否展示替换按钮,文件自身的 showReplace 优先。 | boolean | - |
| showRetry | 上传失败时是否展示重试按钮,文件自身的 showRetry 优先。 | boolean | - |
| showTooltip | 文件名过长时是否展示提示,或配置提示的类型和透传属性。 | boolean | ShowTooltip | true |
| size | 文件大小,单位 KB。 | string | "" |
| status | 文件状态,可选 success、uploadFail、validateFail、validating、uploading、wait。 | FileItemStatus | - |
| style | 文件卡片的内联样式。 | CSSProperties | - |
| uid | 文件唯一标识符,列表内必须唯一。 | string | - |
| url | 文件地址,图片墙用它展示图片,预览时使用。 | string | - |
| validateMessage | 校验信息,ReactNode,优先于文件自身的 validateMessage。 | ReactNode | - |
Interfaces
FileItem Interface
interface FileItem {
event?: Event; // xhr 事件
fileInstance?: File; // 浏览器实际获取到的文件对象,继承自 Blob(https://developer.mozilla.org/zh-CN/docs/Web/API/File)
name: string;
percent?: number; // 上传进度百分比
preview?: boolean; // 是否根据url进行预览
response?: any; // xhr的response, 请求成功时为respoonse body,请求失败时为对应 error
shouldUpload?: boolean; // 是否应该继续上传
showReplace?: boolean; // 单独控制该file是否展示替换按钮
showRetry?: boolean; // 单独控制该file是否展示重试按钮
size: string; // 文件大小,单位kb
status: 'success' | 'uploadFail' | 'validateFail' | 'validating' | 'uploading' | 'wait';
uid: string; // 文件唯一标识符,如果当前文件是通过upload选中添加的,会自动生成uid。如果是defaultFileList, 需要自行保证不会重复
url?: string;
validateMessage?: ReactNode | string;
}
FileItem 只包含上述字段,写入未声明的字段会报类型错误。
RenderFileItemProps Interface
renderFileItem、renderThumbnail、renderPicInfo、renderPicPreviewIcon、renderFileOperation、previewFile 收到的参数,在 FileItem 之上增加文件卡片的属性。renderFileItem 另外收到 key: string,渲染列表项时作为 React key 使用。
interface RenderFileItemProps extends FileItem {
index?: number; // 文件在列表中的位置
listType?: 'picture' | 'list' | 'none';
disabled?: boolean;
style?: CSSProperties; // 来自 itemStyle
showRetry?: boolean;
showReplace?: boolean;
showPicInfo?: boolean;
showTooltip?: boolean | ShowTooltip;
picWidth?: string | number;
picHeight?: string | number;
onRemove?: () => void; // 移除该文件
onRetry?: () => void; // 重新上传该文件
onReplace?: () => void; // 替换该文件
onPreviewClick?: () => void; // 未设置 Upload 的 onPreviewClick 时为 undefined
previewFile?: (renderProps: RenderFileItemProps) => ReactNode;
renderPicInfo?: (renderProps: RenderFileItemProps) => ReactNode;
renderPicPreviewIcon?: (renderProps: RenderFileItemProps) => ReactNode;
renderPicClose?: (renderPicCloseProps: { className: string; remove: (e: MouseEvent) => void }) => ReactNode;
renderThumbnail?: (renderProps: RenderFileItemProps) => ReactNode;
renderFileOperation?: (renderProps: RenderFileItemProps) => ReactNode;
}
RenderFileListTitleProps Interface
fileListTitle 传入函数时的参数类型:
interface RenderFileListTitleProps {
fileList: Array<FileItem>; // 当前文件列表
onClear: () => void; // 清空文件的回调函数
clearText: string; // 清空按钮的默认文案(根据当前语言环境)
}
CropProps Interface
crop 属性传入对象时的配置项:
interface CropProps {
aspectRatio?: number; // 裁切框宽高比,例如 16/9, 1, 4/3
shape?: 'rect' | 'round' | 'roundRect'; // 裁切框形状,默认 'rect'
minZoom?: number; // 最小缩放比例
maxZoom?: number; // 最大缩放比例
zoomStep?: number; // 缩放步长
quality?: number; // 输出图片质量 0-1,默认 0.92
fill?: string; // 填充颜色,默认透明 'rgba(0, 0, 0, 0)'
modalTitle?: string; // 裁切弹窗标题
modalOkText?: string; // 确认按钮文字
modalCancelText?: string; // 取消按钮文字
}
Methods
绑定在组件实例上的方法,可以通过 ref 调用实现某些特殊交互
方法见上方 Upload 的 Methods 表。
Accessibility
Upload组件是一个可交互的控件,在点击或拖拽时触发文件选择,文件选中后会在文件列表内展示状态。
ARIA
- 触发区是
role="button",Enter 与 Space 可激活。触发区内容自带控件(如 Button)时,触发区不加角色,由该控件充当按钮,点击它打开文件选择。该控件被禁用(原生disabled或aria-disabled="true")时仍算使用方自己的控件,触发区不会变成按钮,Enter 与 Space 也不会打开文件选择 - 禁用 Upload 时请同时给该控件传
disabled。达到limit时只禁用该控件、不禁用 Upload,用户仍可移除已上传的文件 - 根元素带
aria-label或aria-labelledby时自动加role="group",使用方显式传了role时以使用方为准 - 文件列表添加
role="list",并用aria-label描述;照片墙的添加按钮在列表之外 - 列表模式下的替换按钮直接作为 Tooltip 的触发器,Tab 键只停靠一次
文案规范
- 上传按钮
- 关于表单按钮的文案规范,参考按钮Button组件的文案规范
- 帮助文本
- 帮助文本使用语句书写规范,首字母大写,可以不需要句号
- 用户出错提示
- 清晰地告诉用户为什么文件无法被上传,并且告知用户如何操作能够成功上传
- 帮助文本使用语句书写规范,首字母大写
- 简洁的用语让用户能够一眼读懂,比如
File size must be less than 20MB,File type must be .gif, .jpg, .png or .svg
设计变量
| Sass 变量 | 值 | 用法 |
|---|---|---|
| $color-upload-text | var(--semi-color-text-0) | 上传文件卡片等默认文本颜色 |
| $color-upload_assist-text | var(--semi-color-text-2) | 上传文件列表标题及文件大小文本颜色 |
| $color-upload-border | var(--semi-color-border) | 图片墙添加按钮及拖拽区域描边颜色 |
| $color-upload_card-bg-hover | var(--semi-color-fill-1) | 上传文件卡片背景色 - 悬浮 |
| $color-upload_card-bg | var(--semi-color-fill-0) | 上传文件卡片背景色 - 默认 |
| $color-upload_card_fail-bg-hover | var(--semi-color-danger-light-hover) | 上传失败文件卡片背景色 - 悬浮 |
| $color-upload_card_fail-bg | var(--semi-color-danger-light-default) | 上传失败文件卡片背景色 - 默认 |
| $color-upload_clear-text | var(--semi-color-primary) | 上传清空按钮文本颜色 |
| $color-upload_drag_area-bg-hover | var(--semi-color-primary-light-default) | 上传可拖拽区域背景颜色 - 悬浮 |
| $color-upload_drag_area-border-hover | var(--semi-color-primary) | 上传可拖拽区域描边颜色 - 悬浮 |
| $color-upload_drag_area_disabled-text | var(--semi-color-disabled-text) | 上传可拖拽区域禁用文本颜色 |
| $color-upload_drag_area-bg | var(--semi-color-tertiary-light-default) | 上传可拖拽区域背景颜色 - 默认 |
| $color-upload_drag_area_icon | var(--semi-color-primary) | 上传可拖拽区域图标颜色 |
| $color-upload_drag_area_tips-text | var(--semi-color-primary) | 上传可拖拽区域提示文本颜色 |
| $color-upload_drag_area_main-text-default | var(--semi-color-text-0) | 上传可拖拽区主要提示文本颜色 - 默认 |
| $color-upload_drag_area_main-text-hover | var(--semi-color-text-0) | 上传可拖拽区主要提示文本颜色 - 悬浮 |
| $color-upload_drag_area_main-text-active | var(--semi-color-text-0) | 上传可拖拽区主要提示文本颜色 - 按下 |
| $color-upload_drag_area_sub-text-default | var(--semi-color-text-0) | 上传可拖拽区次要提示文本颜色 - 默认 |
| $color-upload_drag_area_sub-text-hover | var(--semi-color-text-0) | 上传可拖拽区次要提示文本颜色 - 悬浮 |
| $color-upload_drag_area_sub-text-active | var(--semi-color-text-0) | 上传可拖拽区次要提示文本颜色 - 按下 |
| $color-upload_file_card_fail_info-text | var(--semi-color-danger) | 上传文件卡片失败提示信息文本颜色 |
| $color-upload_file_card_preview_placeholder-bg | var(--semi-grey-3) | 文件卡片默认预览背景颜色 |
| $color-upload_file_card_preview_placeholder-text | var(--semi-white) | 文件卡片默认预览图颜色 |
| $color-upload_file_card_retry-bg | var(--semi-color-white) | 图片墙上传卡片重新上传按钮背景颜色 |
| $color-upload_file_card_retry-text | var(--semi-color-primary) | 图片墙上传卡片重新上传按钮文本颜色 |
| $color-upload-icon | var(--semi-color-tertiary) | 图片墙上传图标加号颜色 |
| $color-upload_pic_add-bg-active | var(--semi-color-fill-2) | 图片墙上传背景色 - 按下 |
| $color-upload_pic_add-bg-hover | var(--semi-color-fill-1) | 图片墙上传背景色 - 悬浮 |
| $color-upload_pic_add-bg | var(--semi-color-fill-0) | 图片墙上传背景色 - 默认 |
| $color-upload_picture_file_card_loading_error-icon | var(--semi-color-danger) | 图片墙上传卡片加载及失败状态图标颜色 |
| $color-upload_picture_file_card_error-border | var(--semi-color-danger) | 图片墙上传卡片失败状态描边颜色 |
| $color-upload_picture_file_card_pic_info-text | var(--semi-color-white) | 图片墙图片信息(序号)文字颜色 |
| $color-upload_picture_file_card_close-icon | var(--semi-color-white) | 图片墙关闭图标颜色 |
| $color-upload_picture_file_card_hover-bg | var(--semi-color-overlay-bg) | 图片墙预览悬浮背景色 |
| $color-upload_preview-icon | var(--semi-color-text-2) | 上传文件卡片文本颜色 |
| $color-upload_retry-text | var(--semi-color-primary) | 上传文件卡片重新上传按钮文本颜色 |
| $color-upload_replace-text | var(--semi-color-white) | 图片墙上传卡片替换按钮文本颜色 |
| $height-upload_file_card | 52px | 上传文件卡片高度 |
| $width-upload_file_card | 250px | 上传文件卡片宽度 |
| $height-upload_file_pic_card | 96px | 图片墙上传卡片高度 |
| $width-upload_file_pic_card | 96px | 图片墙上传卡片宽度 |
| $width-upload_file_card_preview | 36px | 文件卡片预览图标宽度 |
| $height-upload_file_card_preview | 36px | 文件卡片预览图标高度 |
| $width-upload_file_card_preview_img | 36px | 文件卡片预览图片宽度 |
| $width-upload_file_card-icon | 11px | 文件卡片图标宽度 |
| $width-upload_picture_add-border | 2px | 图片墙添加按钮描边宽度 |
| $width-upload_picture_file_card_img | 96px | 图片墙上传卡片宽度 |
| $width-upload_picture_file_card_close | 16px | 图片墙上传卡片删除按钮图标宽度 |
| $width-upload_file_card_retry | 24px | 重新上传按钮宽度 |
| $width-upload_file_card_retry-icon | 13px | 重新上传按钮图标宽度 |
| $width-upload_picture_file_card_loading-icon | 14px | 上传加载 spin 宽度 |
| $width-upload_drag_area-border | 2px | 可拖拽上传描边宽度 |
| $spacing-upload_title_choosen-marginRight | $spacing-tight | 已选择文件标题右侧外边距 |
| $spacing-upload_title-marginBottom | $spacing-extra-tight | 文件列表标题底部外边距 |
| $spacing-upload_file_card_preview-margin | $spacing-tight | 上传文件卡片预览外边距 |
| $spacing-upload_file_card_info_size-marginLeft | $spacing-tight | 上传文件卡片文件大小文本左侧外边距 |
| $spacing-upload_file_card_info_retry-marginLeft | $spacing-tight | 上传文件卡片重新上传按钮左侧外边距 |
| $spacing-upload_file_card_info_progress-marginTop | 4px | 上传文件卡片进度顶部外边距 |
| $spacing-upload_file_card_close-marginLeft | $spacing-tight | 上传文件卡片删除按钮左侧外边距 |
| $spacing-upload_file_card_close-marginRight | $spacing-tight | 上传文件卡片删除按钮右侧外边距 |
| $spacing-upload_file_card_icon-marginRight | $spacing-super-tight | 上传文件卡片图标右侧外边距 |
| $spacing-upload_picture_file_card-gap | $spacing-tight | 图片墙卡片之间边距 |
| $spacing-upload_picture_file_card-marginBottom | $spacing-tight | 图片墙上传卡片底部外边距 |
| $spacing-upload_picture_file_card_close-top | 8px | 图片墙上传卡片删除按钮顶部位置 |
| $spacing-upload_picture_file_card_close-right | 8px | 图片墙上传卡片删除右侧位置 |
| $spacing-upload_picture_file_card_loading_error-bottom | 6px | 图片墙上传卡片加载中图标底部位置 |
| $spacing-upload_picture_file_card_loading_error-right | 6px | 图片墙上传卡片报错图标底部位置 |
| $spacing-upload_picture_file_card_icon_error-top | -1px | 图片墙上传卡片加载中图标顶部位置 |
| $spacing-upload_drag_area-padding | $spacing-base-tight | 可拖拽上传拖拽区域内边距 |
| $spacing-upload_drag_area_main_text-marginBottom | $spacing-extra-tight | 可拖拽上传拖拽标题底部外边距 |
| $radius-upload_file_card | var(--semi-border-radius-medium) | 上传文件卡片圆角 |
| $radius-upload_file_card_preview | var(--semi-border-radius-small) | 上传文件卡片预览图圆角 |
| $radius-upload_picture_add | var(--semi-border-radius-small) | 图片墙上传文件添加按钮圆角 |
| $radius-upload_picture_file_card_img | var(--semi-border-radius-small) | 图片墙上传卡片圆角 |
| $radius-upload_picture_file_card_close | var(--semi-border-radius-circle) | 图片墙上传卡片删除按钮圆角 |
| $radius-upload_file_card_retry | var(--semi-border-radius-circle) | 上传文件卡片重新上传圆角 |
| $radius-upload_drag_area | var(--semi-border-radius-small) | 可拖拽上传拖拽区域圆角 |
| $font-upload_file_card_info_name-fontWeight | $font-weight-bold | 上传文件卡片文件名字重 |
| $font-upload_file_card_info_size-fontWeight | $font-weight-regular | 上传文件卡片文件尺寸字重 |
| $font-upload_drag_area_tips-fontWeight | 600 | 可拖拽上传提示文本字重 |
| $font-upload_picture_file_card_pic_info-fontSize | 12px | 图片墙图片信息字体大小 |
| $font-upload_picture_file_card_pic_info-fontWeight | 600 | 图片墙图片信息文本字重 |
FAQ
- 什么时候会展示重试按钮?
- 当
showRetry为 true,且当前文件是由于网络原因错误导致的上传失败时,会展示重试按钮。其他如校验失败,上传成功等状态是不会展示重试按钮的。
- 当
- 什么时候会展示替换按钮?
- 当
showReplace为true,且当前文件状态为已上传时,会展示替换按钮。
- 当
- Semi Upload把图片存到哪里了?
- Semi Upload不负责图片的保存,当你使用 Upload 组件时需要自定义 action。你可以选择把 action 设置为自己的服务器地址或者图片服务地址。
- Form.Upload props.name无效?
- Form.Field 中有 props.name,Upload也有 props.name,同名 props 会冲突。使用 Form.Upload 时,可以转为使用 props.fileName,避免冲突
- 上传图片后没有调用 XXX 方法?
- 如果你设置了
accept,可以尝试把 accept 属性去掉,然后再看是否调用了改方法。去掉后调用了该方法说明,accept 在当前环境下获取的 file type 与设置的 accept 不符,上传行为提前终止。可以打个断点到 upload/foundation.js checkFileFormat 函数,看下获取的 file.type 真实值是否符合预期。
- 如果你设置了