大文件上传小tips

121 阅读2分钟

背景

其实对于前端,很多需要上传比如几百兆 文件解析的业务场景下,可能会出现哪些问题呢

首先我们可以做的是确定方案

  1. 首先针对于分治思想,可以把大文件拆解为一个个独立的小文件
  2. 为了能够在服务端进行重组,肯定是需要类似于索引序号,类似http2.0的流id机制。

具体流程

  1. 为首先把文件用md5 算法进行加密,进行校验-- md5 本身作为一种消息摘要算法,用来判断文件的内容是否已经被重复上传,这样能够校验文件内容。
  2. 为了能够避免一次性上传过大的文件,那就进行分片--分多少片合适呢
  3. 那服务端接受到之后,为了能够进行文件重组,还需要向其传递对应分块的索引号。
  4. 同时在上传分片的过程中,优先传送对应md5 服务端先来校验一下,当前的分块,有没有被上传过,这样就能加速文件上传,跳过当前分块。
  5. 那怎么提升文件上传的速度,其实可以通过并发上传,分块,不过不能无限并非,因此还需要进行限制,一定时间内,只能并非一部分.

代码实例

function uploadFile(file) {
    const CHUNK_SIZE = 1 * 1024 * 1024; // 分片大小,这里以1MB为例
    const totalChunks = Math.ceil(file.size / CHUNK_SIZE);

    for (let i = 0; i < totalChunks; i++) {
        const chunk = file.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE);
        uploadChunk(chunk, i);
    }
}

function uploadChunk(chunk, chunkIndex) {
    const formData = new FormData();
    formData.append('chunk', chunk);
    formData.append('index', chunkIndex);

    fetch('/upload', { method: 'POST', body: formData })
        .then(response => response.json())
        .then(data => console.log('Chunk uploaded', data))
        .catch(error => console.error('Error in uploading chunk', error));
}

// 调用示例
const file = document.querySelector('input[type="file"]').files[0];
uploadFile(file);

并发限制

  1. 如果当前文件分片过多,那么必然会导致并非数量过多导致卡顿,因此需要限制并发数,以下一个小案例
  2. 基本思路就是预先先进行限制数并非,之后,每一个promise状态成功,就继续请求。
const promiseAllLimit = (promises, limit) => {
  let currentIndex = 0;
  const run = () => {
    return new Promise((resolve, reject) => {
      const runPromise = promises[currentIndex];
      resolve(runPromise())
      currentIndex++
    }).then(() => {
      if (currentIndex < promises.length) {
        run()
      }
    })
  }

  // 依次执行
  for (let i = 0; i < limit; i++) {
    run()
  }
}


// 测试
const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
const tasks = [() => new Promise((resolve) => setTimeout(() => { console.log('1111'); resolve(1) }, 1000)), () => new Promise((resolve) => setTimeout(() => { console.log('2222'); resolve(2) }, 1000)), () => new Promise((resolve) => setTimeout(() => { console.log('3333'); resolve(1) }, 1000)), () => new Promise((resolve) => setTimeout(() => { console.log('4444'); resolve(4) }, 1000))];
promiseAllLimit(tasks, 2)