前端react大文件切片上传

像视频这种大文件上传有很多缺点:

1.后台可能设置了请求时长限制,太久会上传失败

2.NGINX可能设置了文件上传的最大限制导致失败

优点:

1.文件太大分片上传能加快上传速度,提高用户体验

2.能断点续传 如果上次上传失败或者中途离开的话下一次上传过的就不用重头开始了

3.已经上传过的文件根据HASH查询直接秒传

实现流程

这里只贴核心部分代码

数据结构:

  const fileInfoRef = useRef<FileInfoType>({
    HASH: '', // 生成的文件hash值
    fileSuffix: '', // 文件后缀
    alreadyUploadedHashes: [], // 服务器存在的切片hash
    uploadedCount: 0, // 上传成功切片个数
    url: '', // 上传的url
    chunks: [], // 所有切片集合
    file: null, // 当前上传的文件
    totalSize: 0, // 总大小
    currentChunk: null, // 当前上传的切片
  })

1.前端用sparkmd5根据文件生成唯一标识HASH 先将文件转成ArrayBuffer再生成HASH

yarn add spark-md5

    export const useFileToArrayBuffer =
  () =>
  (file: File): Promise<ArrayBuffer> =>
    new Promise((resolve, reject) => {
      try {
        const fileReader = new FileReader()
        fileReader.readAsArrayBuffer(file)
        fileReader.onload = (ev: ProgressEvent<FileReader>) => {
          resolve(ev.target!.result as ArrayBuffer)
        }
      } catch (error) {
        reject(error)
      }
    })
    // 将文件转arraybuffer
    const arrayBuffer = await fileToArrayBuffer(file)
    // 生成文件的HASH值
    const spark = new SparkMD5.ArrayBuffer()
    spark.append(arrayBuffer)
    fileInfoRef.current.HASH = spark.end()
    fileInfoRef.current.fileSuffix = /\.([a-zA-Z0-9]+)$/.exec(file.name)![1]

2.文件切片

文件切片有固定大小和固定数量 可以根据文件大小动态设置

使用Blob.prototype.slice方法对file进行切片

以HASH_chunkIndex.fileSuffix作为文件名

  // 文件切片
  function slice(file: File) {
    fileInfoRef.current.file = file
    let chunkIndex = 0,
      singleSize = file.size > 1024 * 1024 * 5 ? 1024 * 1024 * 5 : file.size, // 默认一个切片,超过5M的话5M一个切片
      splitCount = Math.ceil(file.size / singleSize), // 默认按照固定大小切 向上取整多切一个保证文件完整不丢字节
      chunks = []
    // 如果固定大小数量超出了最大切片数量 则按照固定数量切
    if (splitCount > 100) {
      splitCount = 100
      singleSize = Math.ceil(file.size / 100)
    }
    while (chunkIndex < splitCount) {
      // 切好和文件名存起来 后续请求上传的参数
      chunks.push({
        file: file.slice(chunkIndex * singleSize, ++chunkIndex * singleSize),
        filename: `${fileInfoRef.current.HASH}_${chunkIndex}.${fileInfoRef.current.fileSuffix}`,
      })
    }
    fileInfoRef.current.chunks = chunks
  }

3.上传切片

遍历所有切片上传

  async function uploadChunks() {
    // 上传每一个切片
    for (const chunk of fileInfoRef.current.chunks) {
      fileInfoRef.current.currentChunk = chunk.file
      // 服务器已经存在的切片不再上传
      if (fileInfoRef.current.alreadyUploadedHashes.includes(chunk.filename)) {
        // 更新上传进度
        notifyUpdate()
        continue
      }
      const formData = new FormData()
      formData.append('file', chunk.file)
      formData.append('filename', `${chunk.filename}`)
      await axios.post<UploadSliceResp>(
        'http://127.0.0.1:8888/uploadSlice',
        formData,
        {
          onUploadProgress(progressEvent) {
            // 更新进度
            setUploadPercent(
              Number(
                ((progressEvent.loaded + fileInfoRef.current.totalSize) /
                  fileInfoRef.current.file!.size) *
                  100
              ).toFixed(2)
            )
          },
        }
      )
      await notifyUpdate()
    }
  }

4.边上传边更新切片上传进度

  async function notifyUpdate() {
    fileInfoRef.current.uploadedCount++ // 已上传+1
    // 更新当前总上传大小
    fileInfoRef.current.totalSize += fileInfoRef.current.currentChunk!.size
    // 全部上传完成就合并
    if (
      fileInfoRef.current.uploadedCount === fileInfoRef.current.chunks.length
    ) {
      fileInfoRef.current.url = await mergeChunks()
    }
  }

5.如果全部上传成功 则发送合并切片请求

  async function mergeChunks() {
    const response = await axios.post<UploadSliceResp>(
      'http://127.0.0.1:8888/mergeSlice',
      {
        HASH: fileInfoRef.current.HASH,
        count: fileInfoRef.current.chunks.length,
      },
      {
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      }
    )
    return response.data.servicePath
  }
要在React上传文件到OSS,你需要使用OSS SDK来实现。以下是一个简单的步骤: 1. 安装OSS SDK 你可以使用npm或yarn来安装阿里云OSS SDK。在终端中输入以下命令: ``` npm install ali-oss --save ``` 2. 导入OSS SDK 在你的React组件中导入OSS SDK: ``` import OSS from 'ali-oss'; ``` 3. 创建OSS客户端 在使用OSS SDK之前,你需要创建一个OSS客户端。你需要提供你的Access Key ID、Access Key Secret和Endpoint等信息。 ``` const client = new OSS({ accessKeyId: 'your_access_key_id', accessKeySecret: 'your_access_key_secret', endpoint: 'your_endpoint', bucket: 'your_bucket_name' }); ``` 4. 上传文件 现在你可以使用OSS SDK来上传文件。以下是一个简单的示例: ``` const uploadFile = async (file) => { try { // 生成唯一的文件名 const fileName = Date.now() + '-' + file.name; // 上传文件 const result = await client.put(fileName, file); // 获取文件的URL const fileURL = result.url; // 返回文件的URL return fileURL; } catch (error) { console.log(error); throw error; } } ``` 在这个示例中,我们首先生成一个唯一的文件名,然后使用`client.put`方法来上传文件上传成功后,我们获取文件的URL并返回它。 5. 调用上传方法 最后,在你的React组件中调用上传方法: ``` <input type="file" onChange={async (event) => { const file = event.target.files[0]; const fileURL = await uploadFile(file); console.log(fileURL); }} /> ``` 在这个示例中,我们在一个`<input>`元素的`onChange`事件中调用上传方法。当用户选择一个文件后,我们将文件传递给`uploadFile`方法,并在上传完成后打印文件的URL。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值