手写大文件分片上传

64 阅读1分钟

通过传统的分片上传,发现时间还说有点过长,因为这是一个cpu密集任务,主线程异常忙碌,会导致页面卡顿,所以这里的大文件上传使用了多线程的方式,这样在保证效率的同时页面也不会卡顿。

import { createChunk } from "./worker";
const inputFile = document.querySelector(
  'input[type="file"]'
) as HTMLInputElement;

inputFile.onchange = async (e: any) => {
  const file = e.target.files[0];
  console.time("cutFile");
  //const chunks = await oldCutFile(file);
  const chunks = await newCutFile(file);
  console.timeEnd("cutFile");
  console.log(chunks, "chunks");
};
const CHUNK_SIZE = 1024 * 1024;
// 这种耗时太久
async function oldCutFile(file: any) {
  const chunkCount = Math.ceil(file.size / CHUNK_SIZE);
  const result = [];
  for (let i = 0; i < chunkCount; i++) {
    const chunk = await createChunk(file, i, CHUNK_SIZE);
    result.push(chunk);
  }
  return result;
}

const THREAD_COUNT = navigator.hardwareConcurrency || 4;
async function newCutFile(file: any) {
  return new Promise((resolve, reject) => {
    const chunkCount = Math.ceil(file.size / CHUNK_SIZE);
    const threadChunkCount = Math.ceil(chunkCount / THREAD_COUNT);
    const result = [];
    let finishCount = 0;
    for (let i = 0; i < chunkCount; i++) {
      const worker = new Worker("/src/worker.ts", {
        type: "module",
      });
      let end = (i + 1) * threadChunkCount;
      if (end > chunkCount) {
        end = chunkCount;
      }
      const start = i * threadChunkCount;
      worker.postMessage({
        file,
        CHUNK_SIZE,
        startChunkIndex: start,
        endChunkINdex: end,
      });
      worker.onmessage = (e: any) => {
        for (let i = start; i < end; i++) {
          result[i] = e.data[i - start];
        }
        worker.terminate();
        finishCount++;
        if (finishCount === THREAD_COUNT) {
          resolve(result);
        }
      };
    }
  });
}
import SparkMD5 from "spark-md5";
/**
 *
 * @param file 文件
 * @param index 切片下标
 * @param size 切片大小
 * @returns
 */
export function createChunk(file, index, size) {
  return new Promise((resolve) => {
    const start = index + size;
    const end = start + size;
    const spark = new SparkMD5.ArrayBuffer();
    const fikeReader = new FileReader();
    const blob = file.slice(start, end);
    fikeReader.onload = (e: any) => {
      spark.append(e.target.result);
      resolve({
        start,
        end,
        index,
        hash: spark.end(),
        blob,
      });
    };
    fikeReader.readAsArrayBuffer(blob);
  });
}

onmessage = async (e: any) => {
  const {
    file,
    CHUNK_SIZE,
    startChunkIndex: start,
    endChunkINdex: end,
  } = e.data;
  const proms = [];
  // const a = await createChunk(file, 1, CHUNK_SIZE);
  // console.log(a, "aaaaaaaaaa");

  for (let i = start; i < end; i++) {
    proms.push(createChunk(file, i, CHUNK_SIZE));
  }
  const chunks = await Promise.all(proms);
  postMessage(chunks);
};