文件上传,vue和react都支持
这个案例虽然我是在vue上完成的,但是用的基本上是原生js方法处理,使用react的小伙伴也可以轻松搬运
一、大文件分片上传
1、分片
/**
* 这里我们使用Spark-MD5生成hash值
* 我们不能直接这样使用,我们需要将分片和生成hash操作放到其他线程中处理
* 直接在主线程执行,会导致页面卡顿,所以建议放到web Worker中;
* 原因:在实现MD5编码时会耗时很长,它是一个主线程执行的同步任务且是CPU密集型的任务,计算量很大
* @param {File} file 大文件
* @param {number} index 第几个块
* @param {number} chunkSize 每个块的大小
*/
function createChunks(file, index, chunkSize) {
return new Promise((resolve) => {
const start = index * chunkSize;
const end = start + chunkSize;
const spark = new SparkMD5();
const fileReader = new FileReader();
const blob = file.slice(start, end);
fileReader.onload = (e) => {
if (e.target && e.target.result) {
const result = e.target.result;
if (typeof result === "string") {
spark.append(result);
} else {
const textDecoder = new TextDecoder();
spark.append(textDecoder.decode(result));
}
}
resolve({
start, // 分片的开始字节
end, // 分片的结束字节
index, // 表示第几片
hash: spark.end(), // 标识分块文件的唯一值
blob, // 上传到服务器所需要的File对象
});
};
// 转换成ArrayBuffer目的:SparkMD5库设计用来处理二进制数据
fileReader.readAsArrayBuffer(blob);
});
}
2、校验(断点续传,秒传)
/**
* checkFileExist是一个调用后端接口的操作
* 参数具体根据后端接口调整;
* checkFileExist做两件事:
* 1、上传之前先校验文件是否已上传,若已上传则跳过上传并提示上传成功,实现“秒传”效果;
* 2、校验文件是否全部上传,若未全部上传,则返回已上传的文件Hash值,前端这边根据返回数据过滤出未上传的文件块,实现“断点续传”效果;
*/
const verifyData = await this.checkFileExist(this.fileHash);
// 如果文件已存在,则后端返回的未上传的文件块的集合值是空数值;具体看对接的后端逻辑调整
if (verifyData.isExist && verifyData.chunksHash.length === 0) {
// 实现秒传
alert("文件已上传成功!");
return;
} else {
// 过滤出未上传的文件块 实现断点续传
chunks = chunks.filter((chunk) => {
return !verifyData.chunksHash.includes(chunk.hash);
});
}
// 将分块集合上传给服务器
this.upload(chunks);
3、上传
/**
* 上传文件到服务器
* 上传我们还是采用多线程和并发请求来优化性能
* @param {Array} chunks 切好的文件块
*/
upload(chunks) {
if (chunks.length === 0) return;
// 平均分配给每个Worker的任务量
const workerChunkCount = Math.ceil(chunks.length / this.THREAD_COUNT);
// 计数Worker的完成个数
let finishCount = 0;
for (let i = 0; i < this.THREAD_COUNT; i++) {
const worker = new Worker(new URL("../utils/worker.js", import.meta.url), {
type: "module",
});
let startIndex = i * workerChunkCount;
let endIndex = startIndex + workerChunkCount;
if (endIndex >= chunks.length) {
endIndex = chunks.length;
}
worker.postMessage({
chunks,
startIndex,
endIndex,
isUpload: true,
fileHash: this.fileHash
});
worker.onmessage = () => {
worker.terminate(); // 释放worker
finishCount++;
// 全部上传成功后,通知后端合并分片文件
if (finishCount === this.THREAD_COUNT) {
const params = {
fileHash: this.fileHash,
fileName: this.fileName
}
// 所有分片上传成功后通知服务器合并所有分片
// mergeChunks是后端提供的接口,参数根据后端接口调整
this.mergeChunks(params);
}
};
}
},
如果需要完整代码可以在GitHub上下载:大文件分片上传 觉得对您有帮助的话,帮忙在GitHub上点亮星星,谢谢各位啦!如果需要ts版可以给我留言。