Skip to content

webworker-大文件上传

上次更新 2024年11月26日星期二 7:16:25 字数 0 字 时长 0 分钟

提示

提示:打开控制台查看分片结果

webworker

webworker 是浏览器提供的一种可以在后台运行 JavaScript 代码的技术,它可以在不阻塞主线程的情况下执行耗时操作,比如文件上传、数据处理等。

webworker 的使用非常简单,只需要创建一个 worker 对象,然后向它发送消息,worker 就可以在后台执行代码,并返回结果。

注意

  • 原生 js 可以使用 new Worker
javascript
const worker = new Worker("./worker.js", {
  type: "module",
});
  • 在 vue 中使用需要使用import Worker from "*.js?worker";
javascript
import Worker from "./worker.js?worker";
const worker = new Worker();

分片上传

分片上传是将一个大文件分成多个小文件,然后逐个上传,最后合并。这样可以避免一次性上传大文件导致的网络超时或内存溢出等问题。

分片上传的关键在于如何确定分片的大小和数量,以及如何合并分片。一般来说,分片大小可以根据网络带宽和文件大小来动态调整,数量则可以根据分片大小和文件大小来计算。

webworker 分片上传

cutFile 用于分片
cutFile.js
js
import Worker from "./worker.js?worker";
const CHUNK_SIZE = 1024 * 1024 * 5; // 5MB
let THREAD_COUNT = 4;
if (typeof window !== "undefined") {
  if (navigator.hardwareConcurrency) {
    THREAD_COUNT = navigator.hardwareConcurrency;
  }
}

const chunkList = [];
export const cutFile = async (file) => {
  return new Promise((resolve, reject) => {
    const chunkCount = Math.ceil(file.size / CHUNK_SIZE);
    let chunkIndex = 0;

    for (let i = 0; i < THREAD_COUNT; i++) {
      const worker = new Worker();
      worker.postMessage({
        file,
        index: i,
        chunkSize: CHUNK_SIZE,
      });
      worker.onmessage = (e) => {
        chunkIndex++;
        chunkList.push(e.data);
        if (chunkIndex >= chunkCount) {
          worker.terminate(); // 结束worker
          resolve(chunkList);
        }
      };
      worker.onerror = (e) => {
        reject(e);
      };
    }
  });
};
createChunk 用于创建分片
createChunk.js
js
import SparkMD5 from "./spark-md5.min.js";
export function createChunk(file, index, chunkSize) {
  return new Promise((resolve, reject) => {
    const start = index * chunkSize;
    const end = start + chunkSize;
    const spark = new SparkMD5.ArrayBuffer();
    const fileReader = new FileReader();
    const blob = file.slice(start, end);
    fileReader.onload = function (e) {
      spark.append(e.target.result);
      const hash = spark.end();
      resolve({
        start,
        end,
        index,
        hash,
        chunk: blob,
      });
    };

    fileReader.readAsArrayBuffer(blob);
  });
}
worker webWorker
worker.js
js
import { createChunk } from "./createChunk.js";

onmessage = async function (e) {
  try {
    const { file, index, chunkSize } = e.data;
    const proms = [];
    const chunk = createChunk(file, index, chunkSize);
    proms.push(chunk);
    const chunks = await Promise.all(proms);
    postMessage(chunks);
  } catch (error) {
    postMessage({ error: error.message, md5: window.SparkMD5 });
  }
};