写一个 断点续传 + 分片上传(大文件上传优化) 的前端示例。
这样即使网络中断,文件也可以从已上传的部分继续传,不需要重新传整个大文件。
🔹 分片上传 + 断点续传思路
-
分片切割:把大文件切成固定大小的小块(chunk),例如 5MB/片。
-
生成唯一标识:用文件名 + 文件大小 + hash(可选,md5/sha1)来标识文件,保证续传时能识别已上传的部分。
-
秒传校验:上传前先请求后端,确认哪些分片已存在,只上传缺失的分片。
-
分片上传:前端逐个上传分片。
-
合并分片:上传完成后通知后端合并成一个完整文件。
🔹 原生 JS 分片上传 Demo
javascript
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>分片上传 + 断点续传 Demo</title>
</head>
<body>
<h3>分片上传 + 断点续传</h3>
<input type="file" id="fileInput" />
<button onclick="uploadFile()">上传</button>
<div id="log"></div>
<script>
const CHUNK_SIZE = 5 * 1024 * 1024; // 每片 5MB
async function uploadFile() {
const input = document.getElementById("fileInput");
const file = input.files[0];
if (!file) {
alert("请选择文件");
return;
}
const fileHash = await getFileHash(file); // 用于断点续传的唯一标识
log(`文件哈希: ${fileHash}`);
// 1. 向后端查询已上传的分片
const uploadedChunks = await fetch(`http://localhost:8081/upload/check?fileHash=${fileHash}`)
.then(res => res.json())
.catch(() => []);
log(`已上传分片: ${uploadedChunks}`);
// 2. 切割文件
const chunks = [];
let current = 0;
let index = 0;
while (current < file.size) {
chunks.push({
index,
blob: file.slice(current, current + CHUNK_SIZE),
});
current += CHUNK_SIZE;
index++;
}
// 3. 逐个上传分片(跳过已上传的)
for (let chunk of chunks) {
if (uploadedChunks.includes(chunk.index)) {
log(`分片 ${chunk.index} 已存在,跳过`);
continue;
}
await uploadChunk(fileHash, chunk);
}
// 4. 通知后端合并分片
await fetch("http://localhost:8081/upload/merge", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ fileHash, filename: file.name, totalChunks: chunks.length })
});
log("✅ 上传完成,已请求后端合并分片");
}
function uploadChunk(fileHash, chunk) {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append("file", chunk.blob);
formData.append("fileHash", fileHash);
formData.append("chunkIndex", chunk.index);
const xhr = new XMLHttpRequest();
xhr.open("POST", "http://localhost:8081/upload/chunk");
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = ((e.loaded / e.total) * 100).toFixed(2);
log(`分片 ${chunk.index} 上传进度: ${percent}%`);
}
};
xhr.onload = () => {
if (xhr.status === 200) {
log(`分片 ${chunk.index} 上传完成`);
resolve();
} else {
reject(`分片 ${chunk.index} 上传失败`);
}
};
xhr.onerror = () => reject(`分片 ${chunk.index} 上传错误`);
xhr.send(formData);
});
}
function getFileHash(file) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
// 简单 hash(真实场景可以用 SparkMD5)
let hash = 0, data = new Uint8Array(e.target.result);
for (let i = 0; i < data.length; i++) {
hash = (hash + data[i]) % 99999999;
}
resolve(file.name + "-" + file.size + "-" + hash);
};
reader.readAsArrayBuffer(file.slice(0, 2 * 1024 * 1024)); // 只取前 2MB 计算 hash
});
}
function log(msg) {
document.getElementById("log").innerHTML += msg + "<br/>";
}
</script>
</body>
</html>
🔹 React 版(分片上传 + 断点续传)
如果需要 React 版本,可以用 axios
并结合 onUploadProgress
,同时维护分片上传队列。
🔹 后端接口约定(以 Spring Boot 为例)
-
检查已上传分片
GET /upload/check?fileHash=xxx
返回已上传的分片索引数组
[0,2,5]
。 -
上传分片
POST /upload/chunk
- 参数:
fileHash
,chunkIndex
,file
。
- 参数:
-
合并分片
POST /upload/merge
- 参数:
fileHash
,filename
,totalChunks
。
- 参数:
详细讲 分片上传的原理,从概念到流程再到优势和注意点,一看就懂。
🔹 分片上传原理
1️⃣ 分片上传是什么?
分片上传(Chunk Upload / Slice Upload) :
将一个大文件拆分成若干小块(chunk),逐块上传到服务器,最后由服务器合并成完整文件。
为什么要分片?
-
避免一次性上传大文件导致网络中断、浏览器崩溃或服务器压力过大。
-
可以实现 断点续传:上传中断后只需要重新上传未完成的分片,而不是整个文件。
-
支持大文件上传(几百 MB、几 GB 文件)。
2️⃣ 分片上传的基本原理
分片上传的原理可以分成几个步骤:
Step 1:文件切片
-
将大文件按照固定大小(比如 5MB/片)切成多片。
-
每片分配一个 索引(chunkIndex),用于后续识别和合并。
-
可以生成一个 文件唯一标识(fileHash / fileId):
-
常用方式:
文件名 + 文件大小 + hash(前几 MB)
-
用于判断文件是否已经上传过(秒传)或断点续传。
-
Step 2:上传分片
-
每片单独上传到服务器。
-
请求携带:
-
分片内容 (
file
/chunk
) -
文件标识 (
fileHash
) -
分片索引 (
chunkIndex
)
-
-
服务器收到分片后可以保存到临时目录,例如
uploads/{fileHash}/{chunkIndex}
。
Step 3:断点续传
-
上传前,客户端可以请求服务器:已上传了哪些分片。
-
客户端只上传未完成的分片,提高效率。
-
文件标识和分片索引是判断是否已经上传的关键。
Step 4:合并分片
-
当所有分片上传完成后,客户端通知服务器合并。
-
服务器按分片索引顺序,将所有分片写入到最终文件。
-
完成后可以删除临时分片,释放存储空间。
3️⃣ 分片上传流程图
客户端:
[大文件] --> [分片切割] --> [分片上传1] --> [分片上传2] ... --> [分片上传N] --> [通知合并]
服务器:
[接收分片] --> [保存到临时目录]
[合并分片] --> [生成完整文件]
4️⃣ 优势
-
支持大文件上传:避免一次性请求超时。
-
断点续传:网络中断可继续上传未完成的分片。
-
可并发上传:多个分片可同时上传,提高速度。
-
灵活性高:可实现秒传(判断文件是否已经上传过)或分布式存储(每片可存不同服务器)。
5️⃣ 注意点
-
分片大小选择:
-
太小:分片数量多,请求开销大。
-
太大:网络中断时重传成本高。
-
常用:2~10MB/片。
-
-
文件标识生成:
-
需保证唯一性和稳定性。
-
常用 hash + 文件名 + 大小组合。
-
-
分片索引:
- 上传和合并时一定要按顺序,否则合并文件可能出错。
-
服务器存储:
- 临时存储分片,合并完成后删除,避免磁盘堆积。
-
安全性:
-
限制上传文件类型和大小。
-
对文件名和路径做严格校验,防止目录遍历攻击。
-