分片上传将大文件切块传输,提升稳定性与用户体验;断点续传通过文件哈希标识、服务器进度记录、客户端状态保存等机制,实现中断后续传,解决网络不稳定、服务器压力、超时限制等问题。

表单中的大文件分片上传,简单来说,就是把一个大文件切分成很多小块,然后一块一块地上传到服务器。至于断点续传,那是在这个基础上,如果上传过程中断了,比如网络突然没了,或者用户刷新了页面,下次还能从上次中断的地方继续传,而不是从头再来。这玩意儿对于用户体验和系统稳定性来说,简直是救命稻草。
实现分片上传和断点续传,客户端和服务器端都需要做一些工作。
客户端(前端)
文件切片: 核心是使用
File
slice
webkitSlice
mozSlice
const file = document.getElementById('fileInput').files[0];
const chunkSize = 5 * 1024 * 1024; // 5MB
let currentChunk = 0;
const totalChunks = Math.ceil(file.size / chunkSize);
function uploadChunk() {
const start = currentChunk * chunkSize;
const end = Math.min(file.size, start + chunkSize);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('fileChunk', chunk);
formData.append('fileName', file.name);
formData.append('fileSize', file.size);
formData.append('chunkIndex', currentChunk);
formData.append('totalChunks', totalChunks);
formData.append('fileHash', 'unique-file-hash'); // 用于断点续传和文件校验
// 发送请求到服务器
fetch('/upload/chunk', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
currentChunk++;
if (currentChunk < totalChunks) {
uploadChunk(); // 继续上传下一块
} else {
console.log('文件上传完成!');
// 通知服务器合并文件
fetch('/upload/merge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ fileName: file.name, fileHash: 'unique-file-hash', totalChunks: totalChunks })
});
}
} else {
console.error('上传失败:', data.message);
// 处理失败逻辑,比如重试
}
})
.catch(error => {
console.error('网络错误或服务器异常:', error);
// 错误处理,可能需要记录当前进度以便续传
});
}
// 启动上传
uploadChunk();生成文件唯一标识: 这是断点续传的关键。通常使用文件的MD5或SHA-256哈希值。在文件切片之前,先计算出整个文件的哈希值。这个过程可能耗时,可以在Web Worker中进行,避免阻塞主线程。
// 伪代码:计算文件哈希
function calculateFileHash(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function(e) {
// 使用第三方库如spark-md5或crypto-js计算哈希
const hash = SparkMD5.hashBinary(e.target.result);
resolve(hash);
};
reader.onerror = reject;
reader.readAsBinaryString(file);
});
}
// 在上传前调用:calculateFileHash(file).then(hash => { fileHash = hash; uploadChunk(); });进度管理与状态保存: 客户端需要知道当前上传到了哪一块。断点续传时,启动上传前,先向服务器查询该文件(通过唯一标识)已经上传了多少块。服务器返回已上传的块索引列表,客户端根据这个列表跳过已上传的块,从下一个未上传的块开始。为了防止浏览器关闭导致进度丢失,可以将当前上传进度(文件哈希、已上传块索引)保存在
localStorage
IndexedDB
服务器端(后端)
接收文件块: 服务器端需要一个接口来接收客户端上传的每一个文件块。每个请求应包含文件唯一标识、当前块的索引、总块数等信息。
存储文件块: 接收到文件块后,服务器需要将其临时存储起来。通常是存储在一个以文件唯一标识命名的临时目录下,每个块以其索引命名。 例如:
/tmp/uploads/file_hash_abc/0.chunk
/tmp/uploads/file_hash_abc/1.chunk
记录上传进度: 服务器端需要持久化地记录每个文件的上传进度。这可以通过数据库(如MongoDB、Redis或关系型数据库)来实现,存储文件唯一标识、已上传的块索引列表、文件状态(上传中、已完成)等。当客户端请求续传时,服务器查询这些信息,返回已上传的块列表。
文件合并: 当服务器接收到所有文件块后(通过比对已上传块数与总块数),它需要将这些分散的块按照正确的顺序合并成一个完整的文件。合并完成后,删除临时文件块。
// 伪代码:Node.js 文件合并
const fs = require('fs');
const path = require('path');
app.post('/upload/merge', (req, res) => {
const { fileName, fileHash, totalChunks } = req.body;
const tempDir = path.join(__dirname, 'tmp', 'uploads', fileHash);
const finalFilePath = path.join(__dirname, 'uploads', fileName);
// 创建写入流
const writeStream = fs.createWriteStream(finalFilePath);
let mergedChunks = 0;
function mergeNextChunk(index) {
if (index < totalChunks) {
const chunkPath = path.join(tempDir, `${index}.chunk`);
const readStream = fs.createReadStream(chunkPath);
readStream.pipe(writeStream, { end: false }); // end: false 避免关闭 writeStream
readStream.on('end', () => {
fs.unlink(chunkPath, () => {}); // 删除已合并的块
mergedChunks++;
mergeNextChunk(index + 1);
});
readStream.on('error', (err) => {
console.error('合并块时出错:', err);
writeStream.end(); // 关闭主写入流
res.status(500).send({ success: false, message: '文件合并失败' });
});
} else {
writeStream.end(); // 所有块都已写入,关闭主写入流
// 清理临时目录
fs.rmdir(tempDir, { recursive: true }, () => {});
res.send({ success: true, message: '文件合并成功' });
}
}
mergeNextChunk(0); // 从第一个块开始合并
});校验与清理: 合并完成后,可以对合并后的文件进行哈希校验,确保其与客户端提供的原始文件哈希一致,防止数据损坏。同时,清理临时存储的块文件和相关的进度记录。
讲真,当文件达到几十兆甚至几个G的时候,传统的单文件上传方式简直是灾难。我记得以前做项目,客户要求上传视频,动不动就是几百兆,结果浏览器直接崩溃,或者服务器超时。分片上传的出现,就是为了解决这些实实在在的痛点:
所以,分片上传不仅仅是技术上的优化,更是对用户体验和系统健壮性的一种保障。
断点续传听起来挺玄乎,但拆解开来,无非就是几个关键环节的巧妙配合。
localStorage
IndexedDB
这些点环环相扣,缺一不可。任何一个环节的缺失或设计缺陷,都可能导致断点续传功能形同虚设。
虽然分片上传和断点续传听起来很美,但在实际落地过程中,总会遇到一些意想不到的坑,或者说,需要更细致考量的地方。这就像你搭一个复杂的乐高模型,有些小零件总是不那么容易卡到位。
localStorage
IndexedDB
这些挑战没有一劳永逸的解决方案,更多的是根据具体业务场景和技术栈进行权衡和取舍。但提前预见到它们,至少能让开发过程少走很多弯路。
以上就是表单中的大文件分片上传怎么实现?如何断点续传?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号