上传组件
说明
本组件只包含上传逻辑与渲染上传按钮的部分。如果需要显示标准的文件列表,请对接 @beisen-phoenix/file-list 显示文件列表,如果要预览图片,请对接 @beisen-phoenix/img-preview 组件
upload 目前有四个相关组件
- upload 上传核心逻辑
- file-list 文件列表
- upload-button-block,特殊样式的上传按钮
- img-preview 文件预览
为什么使用受控组件
在开发上传组件的过程中,发现业务场景比较多,我们不能通过一个组件满足所有的需求,所以对整个上传行为进行了一次拆分,期中本组件负责,从选择文件到上传过程中的逻辑,但是不负责渲染文件列表。
同时,因为上传的时候数据状态本身也比较复杂,通过受控组件的方式,把组件状态的变化实时的告知父组件,由父组件根据自身的情况对数据进行处理。所以 Upload 组件需要接受两个基本的参数
导出说明
默认导出为 Upload 只包含上传逻辑,需要配合 children 属性,包裹元素使用 还导出了 UploadDnd 支持拖拽上传样式,可以查看示例
API
上传逻辑和上传样式的公共 api
参数 | 说明 | 类型 | 默认值 | 是否必传 |
---|---|---|---|---|
url | 上传接口地址(只支持 post) | string 或 ((f: any, opt?: any) => string) | 无 | yes |
multiple | 是否允许多选 | boolean | true | no |
maxSize | 文件体积限制(单位M) | number | 无 | no |
maxFiles | 文件个数限制 | number | 无 | no |
onError | 发生错误回调 | (err: any) => void | 无 | yes |
files | 文件列表 | IUploadFileType[] | 无 | true |
onChange | 单个文件上传完成时的回调 | (f: IUploadFileType[]) => void; | 无 | true |
onEachComplete | 文件状态发生变化时的回调 | (data: Partial |
无 | 否 |
accept | 接受的文件类型格式 | string 或者 string[] | 无 | 否 |
parseResponse | 格式化上传接口返回的数据 | IParseResponse | 无 | true |
validator | 自定义的文件验证方法,在选择文件后调用,可以按照自己的需求过滤 | (file: File[]) => File[] 或者 Promise<File[]> | 无 | 否 |
chunkUploadEnabled | 是否开启分片上传(注意,还需要后端接口支持分片上传) | boolean | ((file: File) => boolean) | 否 | |
chunkSize | 分片大小 | number | 5 * 1024 * 1024 | 否 |
parallelUploadLimit | 并行上传数, 为1时即可实现串行上传 | number | 5 | 否 |
prepareChunkUpload | 分片上传前置准备逻辑,如果开启分片上传,需要先从服务端获取fileId,后续分片上传会带上该id | (file: File) => Promise<{ fileId: string; [key: string]: any }> | 无 | 开启分片上传时必传 |
disabled | 是否禁用 | boolean | false | 否 |
UploadDnd 特有 api
参数 | 说明 | 类型 | 默认值 | 是否必传 |
---|---|---|---|---|
width | 宽度 | string | 无 | 否 |
height | 高度 | string | 无 | 否 |
error | 是否报错 | boolean | false | 否 |
size | 尺寸(分为大号和小号) | EDndSize | EDndSize.small | 否 |
text | 显示主文本 | ReactNode | 无 | 否 |
subText | 副标题 (仅 size=EDndSize.big)时有效 | ReactNode | 无 | 否 |
lang | 当前语言环境(zh_CN,en_US,zh_TW) | string | zh_CN | 否 |
translation | 自定义语言包,详情见下方 | object | - | 否 |
[5.0.41] 2021-03-08
- fix 增加参数:customComposeFiles;支持用户自定义文件对比规则。
[version]
- feat 添加参数lang,设置当前语言环境
translation
translation: {
upload: '将文件拖至此处,或点此上传'
}
- files: UploadFileType[] 文件列表
- onChange: (files: UploadFileType[]) => void 当文件状态发生变化的时候的回调
期中 UploadFileType 的类型定义为
interface IParseResponse {
(data: any): Partial<IUploadFileType>;
}
enum EDndSize {
big = "big",
small = "small"
}
interface UploadFileType {
name: string; //文件名称
status: EStatus; //当前状态
mediaType: string; // 文件格式
previewUrl?: string; //预览地址
downloadUrl?: string; // 下载地址
percent?: number; // 上传进度
raw?: File; // 源文件数据
id?: string; // 后端数据标识
lid?: string; // 前端数据标识
}
参数 (interface 定义)
interface UploadProps {
multiple?: boolean; // 是否允许多选
maxSize?: number; // 文件体积限制
maxFiles?: number; // 文件个数限制
url: Url; // 上传接口地址,支持post
onError?: OnError; //发生错误回调
children: JSX.Element; //children 上传按钮的react element
files?: IUploadFileType[]; //文件列表
onChange: OnChange; //文件状态发生变化时的回调
onEachComplete?: OnEachComplete; //单个文件上传完成时的回调
accept?: string | string[]; //接受的文件类型格式
parseResponse?: IParseResponse;
validator?: TValidator; //自定义的文件验证方法,在选择文件后调用,可以按照自己的需求过滤
}
接受的文件类型:参数 accept
详细的信息请参考 https://github.com/okonet/attr-accept, 不过请注意 mime 类型在不同系统下表现是不一样的,比如 CSV 类型的文件在 MacOS 下是 text/plain,但在 window 系统下是 application/vnd.ms-excel。同时在一些场景下可能都不是 mine 类型。参考:https://github.com/react-dropzone/react-dropzone/issues/276
文件类型示例
- .png 只接受 png 图片
- *image/\ ** 接受所有类型的图片
- ["image/*", "video/mp4"] 只接受图片和 mp4 的视频
https://github.com/okonet/attr-accept/blob/master/test/index.js
关于 id 与 lid
在文件数据中允许同时存在 id 与 lid, lid 是上传文件的名称+当前的时间戳,是为了方便识别当前上传的文件,当文件上传到服务器,就需要生成一个服务端的 id,就是 id 所代表的意义。
示例
import React, { useState } from "react";
import Upload, { UploadFileType } from "../../src";
import FilePreview from "@beisen-phoenix/file-list";
const App: React.FunctionComponent<any> = props => {
let [files, setFiles] = useState<UploadFileType[]>([]);
const handleError = err => {
console.log(err, "error");
};
const handleChange = (d: UploadFileType[]) => {
setFiles(d);
};
const handleDelete = (data: UploadFileType) => {
let idx = files.findIndex(item => item.id === data.id);
if (idx !== -1) {
setFiles([...files.slice(0, idx), ...files.slice(idx + 1)]);
}
};
return (
<div>
<FilePreview files={files} edit={true} onDelete={handleDelete} />
<Upload
multiple={true}
onError={handleError}
limit={10}
onChange={handleChange}
files={files}
>
<button>上传</button>
</Upload>
</div>
);
};
export default App;