Skip to content

liudichen/s3-uploader

Repository files navigation

@iimm/s3-uploader

NPM version NPM downloads

学习阶段自定义的尝试用来进行本地minio s3 分片(分片默认大小是为5M)上传的react组件;使用了mui ahooks tabler-icons等,支持并发分片上传和断点续传(需后端支持),为了安全起见,除了分片上传阶段,不会与s3服务器直接交互,分片上传阶段的直接交互应使用临时授权的preSignedUrl。当然可以通过传入自定义的s3PreUploadRequest、s3PartUploadRequest、s3CompleteUploadRequest等来实现自定义方式。

文件分片上传流程

默认情况下是分片上传:

文件校验(validate,使用fileCheck和isSameFile进行检查) => md5计算 => 初始化(preUpload,与后端交互) => 分片上传(partUpload,直接上传到s3服务器) => 合并文件(completeUpload,与后端交互) => 完成

预处理阶段需要后端进行检查该文件是否已上传过(根据md5和文件size),如果:

  • ①已上传过且完成了则直接返回相应信息
  • ②已上传过但未完全上传则返回各分片的上传进度信息,其中未上传完成的分片会返回直接上传到s3的url
  • ③未上传则直接创建分片上传任务,返回

注意:返回的分片任务是完整的PartNumber从1到Math.ceil(size/chunkSize)的任务信息

分片上传阶段会并发(默认为3)发起分片上传(允许暂停),全部完成后通知后端,后端会通知s3服务器合并,完成后后端返回url等信息

文件直接上传

directUpload=true时,在文件大小不大于directUploadMaxSize(默认值为4M)会跳过分片上传和合并文件阶段。

文件校验(validate,使用fileCheck和isSameFile进行检查) => md5计算 => 初始化(preUpload,与后端交互,同时完成文件上传) => 完成

效果图

预览图

Interface

完整类型见类型定义: src/interface/index.ts [https://github.com/liudichen/s3-uploader/blob/master/src/interface/index.ts]

单个文件条目的类型:

interface UploadFile {
  file?: File;
  /**文件名, File.name*/
  name: string;
  /**文件类型,即 File.type */
  type?: string;
  /** 文件上传或校验过程的错误文本 */
  err?: string;
  /** 错误类型,揭示哪个阶段发生了错误 */
  errType?: "validate" | "md5" | "preUpload" | "completeUpload" | "partUpload";
  /** 已上传完毕? */
  done?: boolean;
  md5?: string;
  /** 文件上传任务的数据库表id */
  id?: string;
  /** 文件上传后的归档数据库id */
  s3?: string;
  /**分片上传任务的s3 UploadId */
  uploadId?: string;
  /**文件大小,即 File.size */
  size: number;
  /**分片总数量,仅文件之前未完整上传时有(可选) */
  count?: number;
  /** 服务器中在本次上传前已存在上传完成的文件?*/
  exist?: boolean;
  /** 后端返回分片上传任务(如果有任务则会完整返回PartNumber从1到Math.ceil(size/chunkSize)所有分片的任务信息,未完成的会有直接上传的url),done=true时会被清空 */
  parts?: S3PreUploadPart[];
  /**文件是否被选择,当开启了文件选择时有意义
   * @default false
   */
  checked?: boolean;
  /**当成功时返回的存储桶名 */
  Bucket?: string;
  /**当成功时返回的实际文件路径,注意文件名可能与当前文件名不一致 */
  Key?: string;
  /**当成功时返回的版本id */
  VersionId?: string;
  /** 当成功时返回的在s3中的临时访问url */
  url?: string;
}

组件与子组件(每个文件)公用的部分props:

interface S3RelateItemProps {
  /** 启用直接上传?(file.szie小于等于directUploadMaxSize)
   * @default false
   */
  directUpload?: boolean;
  /**
   * 直接上传最大文件大小
   * @default 1194304='4M'
   */
  directUploadMaxSize?: number;
  /**分片上传的分片大小,minio默认为5M
   * @default 5242880='5M'
   */
  chunkSize?: number;
  /** 文件可预览? */
  preview?: boolean;
  /**预览文件的组件(推荐是弹窗之类不占用文档流) */
  PreviewRender?: FilePreviewComponent;

  /**显示文件可选择项 */
  selectable?: boolean;
  /**文件多选还是单选
   * @default 'multiple'
   */
  selectType?: "single" | "multiple";
  /** 上传文件来源平台*/
  platform: string;
  /** 平台上的某一应用 */
  app?: string;
  /**手动指定桶名,实际并不一定会使用(如果其它桶中已上传的情况下) */
  bucket?: string;
  /**文件在桶中的存储路径 */
  filePrefix?: string;
  /**文件上传前检查文件在服务器中状态或任务的url */
  s3PreUploadUrl: string;
  /**文件分片全部上传后通知合并的url */
  s3CompleteUploadUrl: string;
  /**取消分片任务的url */
  s3AbortUploadUrl?: string;
  /**文件上传前的请求,检查服务器是否已存在文件,如果存在直接返回结果,不存在则返回创建的分片上传任务,有内置的,需要自定替换 */
  s3PreUploadRequest?: S3PreUploadRequestFn;
  /**向s3生成的单个分片上传任务上传文件的请求,按api这应该是个PUT请求,url是s3PreUploadRequest返回的parts中携带的 */
  s3PartUploadRequest?: S3PartUploadRequestFn;
  /** 当所有分片上传后通知服务进行分片合并的请求 */
  s3CompleteUploadRequest?: S3CompleteUploadRequestFn;
  /** 取消分片上传任务的请求,当前并没有去实现,采用的是任务设置失效时间的方式 */
  s3AbortUploadRequest?: S3AbortUploadRequestFn;

  /**当返回0时表示md5在计算过程中手动终止,false表示出错了 */
  md5Getter?: Md5GetterFn;

  /**axios baseURL */
  baseURL?: string;
  /**axios请求的超时时间(ms)
   * @default 15000 = 15s
   */
  timeout?: number;

  /** 渲染文档图标的组件,可选,有内置的默认组件*/
  FileIconRender?: ComponentType<FileIconRenderProps>;

  /**分片上传并发数量限制
   * @default 3
   */
  limit?: number;
  /** 请求及请求返回的url地址在请求前或存进value前的转换函数,如果不传或没有返回值,则使用原始值 */
  urlConvert?: UrlConvertFn;
  /**达到并发限制时,等待多少ms再次进行检查是否达到并发数量限制
   * @default 1000
   */
  chunkWaitTime?: number;
  /**文件上传的额外的s3 MetaData */
  meta?: Record<string, number | string>;
  uploader?: string;
  uploaderName?: string;
}

父组件props:

interface S3UploaderProps
  extends Partial<Omit<DropzoneOptions, "onDropAccepted" | "multiple">>,
    S3RelateItemProps {
  value?: UploadFile[];
  onChange?: (v: UploadFile[]) => void;
  defaultValue?: UploadFile[];
  error?: boolean;
  readOnly?: boolean;

  /**返回候选可以上传的文件数组 */
  onDropAccepted?:
    | (<T extends File>(files: T[], event: DropEvent) => Promise<File[]>)
    | (<T extends File>(files: T[], event: DropEvent) => File[]);

  /**应用于根组件 Stack */
  className?: string;

  /**应用于上传或拖拽区根div组件 */
  uploadZoneClassName?: string;

  /** 应用于每个子文件组件的根Box组件 */
  uploadItemClassName?: string;

  /** 判断是否是同一文件的方法,如果返回true则该文件与已有文件相同,不能添加 */
  isSameFile?: IsSameFileFn;

  /**触发DropZone的元素节点 */
  dropZoneTrigger?: ReactNode;

  /**校验文件本身是否满足要求,如果不满足返回不满足的字符串否则返回空字符串或无返回值,不满足要求的 */
  fileChecker?: ((file: File) => string | undefined) | ((file: File) => Promise<string | undefined>);
}

LICENSE

MIT

About

自用的react s3 分片文件上传组件

Resources

License

Stars

Watchers

Forks

Packages

No packages published