El front-end reacciona a la carga de fragmentos de archivos grandes

Cargar archivos grandes como vídeos tiene muchas desventajas:

1. Es posible que haya un límite de tiempo de solicitud establecido en segundo plano. Si es demasiado largo, la carga fallará.

2.Es posible que NGINX haya establecido un límite máximo para la carga de archivos, lo que provoca fallas

ventaja:

1. Si el archivo es demasiado grande, cargarlo en partes puede acelerar la carga y mejorar la experiencia del usuario.

2. Capaz de reanudar la carga en los puntos de interrupción. Si la última carga falla o se queda a la mitad, no es necesario que comience de nuevo desde la siguiente carga.

3. Los archivos que se han cargado se cargan directamente al instante según la consulta HASH.

Proceso de implementación

Aquí solo se publica el código principal.

estructura de datos:

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

1. La interfaz usa sparkmd5 para generar un identificador único HASH basado en el archivo. Primero convierta el archivo en un ArrayBuffer y luego genere el HASH.

hilo agregar chispa-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. Corte de archivos

Los fragmentos de archivos tienen un tamaño fijo y un número fijo que se puede configurar dinámicamente según el tamaño del archivo.

Utilice el método Blob.prototype.slice para dividir el archivo

Utilice HASH_chunkIndex.fileSuffix como nombre de archivo

  // 文件切片
  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. Sube porciones

Recorre todos los sectores para cargar

  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. Actualice el progreso de carga del segmento durante la carga.

  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. Si todas las cargas se realizan correctamente, envíe una solicitud de fusión de fragmentos

  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
  }

Supongo que te gusta

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