Skip to content

简要说明

本章只做前端使用说明和后端的接口规范说明。

如果你需要了解具体的实现原理,请到大文件上传实现原理章节;

如果你需要了解相关的配置项含义,请到配置项章节。

安装

你可以使用 npm 引入本包

bash
npm i @zfile/upload

使用

通过本包导出 useFileUpload 这个hook,并传入一些必要的配置项,并进一步 解构出 upload方法,用于后续具体的文件上传操作。

以下代码为最简单的使用方式:

js
import {useFileUpload} from "@zfile/upload"
const {upload} = useFileUpload({
  actions: {
    baseURL: "http://localhost:3000/api",
    check: {
      action: '/check',
      method: 'post',
      transformData(file) {
        return {
          fileHash: file.hash
        }
      },
      transformResponse(response, chunks, file) {
        const data = response.data
        if (data.success) {
          return {
            success: true,
            response,
            uploadedChunks: chunks,
            chunks: []
          }
        }
        const list: { hash: string; index: number }[] =
          data.data?.map((item: Record<string, any>) => ({
            hash: item.chunk_hash,
            index: item.chunk_number
          })) ?? []
        list.sort((pre, cur) => pre.index - cur.index)
        const indexSet = new Set<number>()
        const hashSet = new Set<string>()

        for (let i = 0; i < list.length; i++) {
          indexSet.add(list[i].index)
          hashSet.add(list[i].hash)
        }
        const leftChunks = chunks.filter((chunk) => {
          return !(indexSet.has(chunk.index) && hashSet.has(chunk.hash as string))
        })
        const uploadedChunks = chunks.filter((chunk) => {
          return indexSet.has(chunk.index) && hashSet.has(chunk.hash as string)
        })
        const check = !leftChunks.length
        return {
          success: check,
          response,
          chunks: leftChunks,
          uploadedChunks
        }
      },
      transformError(error, isCancel) {
        return {
          success: false,
          error,
          isCancel
        }
      }
    },
    upload: {
      action: '/upload',
      method: 'post',
      transformData(chunk, file) {
        const formData = new FormData()
        formData.append('total', `${file.total}`)
        formData.append('chunkNumber', `${chunk.index}`)
        formData.append('chunkSize', `${chunk.size}`)
        formData.append('fileName', `${file.name}`)
        formData.append('fileSize', `${file.size}`)
        formData.append('fileHash', `${file.hash}`)
        formData.append('chunkHash', `${chunk.hash}`)
        return formData
      },
      transformResponse: (response) => {
        return {
          success: true,
          response
        }
      },
      transformError: (error, isCancel) => {
        return {
          success: false,
          error,
          isCancel
        }
      }
    },
    merge: {
      action: '/merge',
      method: 'post',
      transformData(file, chunks) {
        return {
          total: file.total,
          md5: file.hash,
          fileName: file.name
        }
      },
      transformResponse: (response) => {
        return {
          success: true,
          response
        }
      },
      transformError: (error, isCancel) => {
        return {
          success: false,
          error,
          isCancel
        }
      }
    }
  },
  onFileChange(file,files,type){
    //当文件确认上传或者删除文件时触发
  },
  onProgress(percentage,file,files){
    //文件进度改变时触发
  },
  onSuccess(file){
    //某个文件上传成功后触发
  }
})

//具体上传时,请获取到原生的File对象,通过下述的方式传入:
upload(file)

上述选项将在选项参数一章做具体的说明。

INFO

由于大文件上传需要使用 三个接口实现,具体请求参数要求和返回响应视具体的业务而定; 因此,本库将此部分的逻辑暴露出来,以实现整体大逻辑和具体业务上的解耦。

数据库的设计

要实现完整的大文件上传,后端需要设计两张表,一张为切片表(chunk_list),一张为文件表(file_list),相关字段如下表所示:

切片表

字段名字段类型含义
idint自增id
file_hashvarchar(255)文件唯一hash
chunk_numberint分片序号
file_namevarchar(255)文件名
createdAtdatetime分片上传时间
chunk_totalint分片大小
total_sizeint文件大小
chunk_hashvarchar(255)全量内容计算hash,分片自己的hash值

文件表

字段名字段类型含义
idint自增id
file_hashvarchar(255)文件hash
file_sizeint文件总大小
file_namevarchar(255)文件名称
createdAtdatetime文件上传时间
file_pathvarchar(255)文件本地存放路径

接口设计

大文件上传需要三个接口 checkApi、uploadApi、和mergeApi,其作用分别是:检查分片上传完成情况和是否秒传、上传分片、合并上传的分片(或在秒传后上传一些业务字段)。

作者本人用nodejs写了一套API,并提交到了Github仓库,可以阅读我的后端代码来进一步设计自己的后端流程。

三个接口的请求和请求参数如下所示

检查接口

  • 请求路径 /api/check

  • 请求方法 post

  • 请求对象(body application/json)

    字段类型含义
    fileHashstring文件唯一hash
  • 请求响应(data application/json)

字段类型含义
successboolean文件是否已经上传并存在,true即是秒传
datanumber[]分片下标数组,用于前端判断哪些分片已经上传哪些还未上传

上传接口

  • 请求路径 /api/upload
  • 请求方法 post
  • 请求对象(body. FormData)
字段类型含义
totalnumber文件总大小
chunkNumbernumberchunk序号
chunkSizenumberchunk大小
fileNamestring文件名
fileSizenumber文件大小
fileHashstring文件的hash值
chunkHashstringchunk的hash值
fileBlobchunk块
  • 请求响应(data application/json)

    字段类型含义
    successboolean切片是否上传成功
    datanull预留返回数据

合并接口

  • 请求路径 /api/merge

  • 请求方法 post

  • 请求对象(body application/json)

    字段类型含义
    totalnumber合并的文件总大小
    md5string合并的文件hash值
    fileNamestring合并的文件名
  • 请求响应(data application/json)

字段类型含义
successboolean文件合并是否成功

Released under the MIT License.