前端开启多线程实现大文件上传
一、概念
文件切片上传的简单理解:前端先将文件分割成多份,再进行上传,由后端进行切片合并操作

二、具体实现
1.HTML 结构
<template>
<form action="/upload" method="post"></form>
<input type="file" name="file" />
</form>
</template>
2.监听文件选择事件
import { cutFile } from "./cutFile.js";
const inpFile = document.querySelector('input[type="file"]');
inpFile.onchange = async e => {
const file = e.target.files[0];
console.time("cutFile"); // 记录时间
const chunks = await cutFile(file);
console.timeEnd("cutFile");
};
3.获取文件 hash 值工具函数
使用 spark-md5 来获取文件 hash 值
import "./spark-md5.min.js";
function createChunk(file, index, chunkSize) {
return new Promise(resolve => {
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 = e => {
spark.append(e.target.result);
resolve({ start, end, index, hash: spark.end(), blob });
};
fileReader.readAsArrayBuffer(blob);
});
}
4.进行文件切片并开启多线程
- 协商分片文件的大小
- 利用 cpu 多线程进行文件切片
const CHUNK_SIZE = 1024 * 1024 * 5;
const THREAD_COUNT = navigator.hardwareConcurrency || 4;
function cutFile(file) {
return new Promise(resolve => {
const chunkCount = Math.ceil(file.size / CHUNK_SIZE);
const threadChunkCount = Math.ceil(chunkCount / THREAD_COUNT);
const result = [];
let finishCount = 0;
for (let index = 0; index < THREAD_COUNT; index++) {
// 分配线程任务
const worker = new Worker("worker.js", { type: "module" });
const start = index * threadChunkCount;
let end = (index + 1) * threadChunkCount;
if (end > chunkCount) end = chunkCount;
// 发送数据到 Worker
worker.postMessage({ file, start, end, CHUNK_SIZE });
// 监听 Worker 发送的消息
worker.onmessage = function (event) {
result[index] = event.data;
worker.terminate();
finishCount++;
if (finishCount === THREAD_COUNT) resolve(result.flat());
};
}
});
}
5.线程任务
利用自己的 cpu 开启线程,完成计算任务,并返回结果。
// 监听主线程发送的消息
import { createChunk } from "./createChunks.js";
// 监听主线程发送的消息
self.onmessage = async function (event) {
const { file, start, end, CHUNK_SIZE } = event.data;
const result = [];
for (let i = start; i < end; i++) {
const prom = await createChunk(file, i, CHUNK_SIZE);
result.push(prom);
}
const chunks = await Promise.all(result);
// 发送结果回主线程
self.postMessage(chunks);
};
三、总结
通过上述的代码,我们已经基本实现了前端大文件上传、续传的功能,这其中还有许多可以完善的点,就交给小伙伴们自己挖掘了,感谢大家阅读。