Upload de fatia de arquivo grande de reação de front-end

O upload de arquivos grandes, como vídeos, tem muitas desvantagens:

1. Pode haver um limite de tempo de solicitação definido em segundo plano. Se for muito longo, o upload falhará.

2.NGINX pode ter definido um limite máximo para upload de arquivos, causando falha

vantagem:

1. Se o arquivo for muito grande, o upload em partes pode acelerar o upload e melhorar a experiência do usuário.

2. Capaz de retomar o upload em pontos de interrupção. Se o último upload falhar ou sair no meio do caminho, você não precisará recomeçar a partir do próximo upload.

3. Os arquivos que foram carregados são carregados diretamente e instantaneamente com base na consulta HASH.

Processo de implementação

Apenas o código principal é postado aqui

estrutura de dados:

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

1. O front-end usa sparkmd5 para gerar um identificador exclusivo HASH baseado no arquivo. Primeiro converta o arquivo em um ArrayBuffer e depois gere o HASH.

fio adicionar 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. Fatiamento de arquivo

As fatias de arquivo têm um tamanho fixo e um número fixo que pode ser definido dinamicamente de acordo com o tamanho do arquivo.

Use o método Blob.prototype.slice para fatiar o arquivo

Use HASH_chunkIndex.fileSuffix como nome do arquivo

  // 文件切片
  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. Carregar fatias

Percorra todas as fatias para fazer upload

  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. Atualize o progresso do upload da fatia durante o upload

  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. Se todos os uploads forem bem-sucedidos, envie uma solicitação de mesclagem de fatia

  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
  }

Acho que você gosta

Origin blog.csdn.net/Suk__/article/details/129288082
Recomendado
Clasificación