断点续传时如何准确记录和恢复文件上传进度?

UX-文瑞 阅读 54

我正在用 axios 做大文件分片上传,前端按 5MB 切片,后端也支持合并。但用户刷新页面后,怎么知道哪些分片已经传成功了?我试过把已上传的分片索引存到 localStorage,但有时候网络波动导致部分分片其实没传完,结果续传时跳过了这些“假成功”的分片。

有没有更可靠的方式?比如每次上传前先向服务端查询已接收的分片?如果是的话,这部分逻辑该怎么组织?下面是我当前切片上传的简化代码:

const uploadChunk = async (chunk, index) => {
  const formData = new FormData();
  formData.append('chunk', chunk);
  formData.append('index', index);
  formData.append('fileId', fileId);

  try {
    await axios.post('/upload', formData);
    // 这里直接标记为成功,可能有问题?
    uploadedChunks.add(index);
    localStorage.setItem('uploaded', JSON.stringify([...uploadedChunks]));
  } catch (err) {
    console.error('上传失败', index);
  }
};
我来解答 赞 20 收藏
二维码
手机扫码查看
2 条解答
欧阳志远
断点续传时遇到的问题确实有点棘手,特别是网络波动导致的假成功情况。为了更可靠地记录和恢复文件上传进度,我们可以采取一个两步走的策略:首先,在上传每个分片之前,向服务器查询该分片是否已经成功接收;其次,确保服务器能够准确地返回已接收的分片信息。

第一步,我们需要修改 uploadChunk 函数,在实际上传分片之前,先向服务器发送一个请求来检查该分片是否已经存在。这样可以避免网络抖动导致的假成功问题。

第二步,服务器需要提供一个接口,用于返回已接收的分片信息。这个接口可以根据文件ID来返回一个已上传的分片索引数组或者位图,这样前端就可以准确地知道哪些分片已经成功上传。

下面是调整后的代码示例:

// 定义一个函数来获取已上传的分片索引
const getUploadedChunks = async (fileId) => {
try {
const response = await axios.get(/uploaded-chunks?fileId=${fileId});
return new Set(response.data); // 假设服务器返回的是一个数组 [0, 1, 3] 表示第0, 1, 3个分片已上传
} catch (err) {
console.error('获取已上传分片失败', err);
return new Set(); // 返回空集合表示没有已上传的分片
}
};

// 修改后的 uploadChunk 函数
const uploadChunk = async (chunk, index, fileId) => {
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('index', index);
formData.append('fileId', fileId);

try {
await axios.post('/upload', formData);
uploadedChunks.add(index);
localStorage.setItem('uploaded', JSON.stringify([...uploadedChunks]));
} catch (err) {
console.error('上传失败', index);
}
};

// 整合的上传逻辑
const startUpload = async (file, fileId) => {
const chunkSize = 5 * 1024 * 1024; // 5MB
let offset = 0;
const chunks = [];

// 获取已上传的分片索引
uploadedChunks = await getUploadedChunks(fileId);

while (offset < file.size) {
const end = Math.min(offset + chunkSize, file.size);
const chunk = file.slice(offset, end);
const index = Math.floor(offset / chunkSize);

if (!uploadedChunks.has(index)) {
console.log(开始上传分片 ${index});
await uploadChunk(chunk, index, fileId);
} else {
console.log(分片 ${index} 已经上传,跳过);
}

offset += chunkSize;
}
};


这段代码首先通过 getUploadedChunks 函数从服务器获取已上传的分片索引,然后在 startUpload 函数中遍历所有分片,只上传那些尚未上传的分片。这样可以确保即使在网络波动的情况下,也不会出现假成功的分片被跳过的情况。

服务器端需要实现 /uploaded-chunks 这个接口,根据文件ID返回已上传的分片索引数组。具体实现取决于你的后端技术栈,但思路是相似的:检查存储中是否有某个分片,并返回已存在的分片索引列表。
点赞
2026-03-24 09:14
永香酱~
你这个问题我遇到过,localStorage记录上传状态确实不可靠。更靠谱的做法是让服务端维护上传状态,每次上传前先查询已上传的分片。

具体可以这样改:

1. 上传前先请求服务端获取已上传分片列表:
const getUploadedChunks = async (fileId) => {
const res = await axios.get(/upload-status?fileId=${fileId});
return new Set(res.data.uploadedChunks); // 服务端返回已上传的索引数组
}


2. 修改你的上传逻辑,先用这个接口初始化已上传分片集合:
const uploadedChunks = await getUploadedChunks(fileId);


3. 上传成功后让服务端也记录状态(需要改服务端接口):
await axios.post('/upload', formData);
// 成功后调用确认接口
await axios.post('/upload-confirm', { fileId, index });


服务端需要实现这两个接口:
- GET /upload-status 返回指定文件已确认上传的分片索引
- POST /upload-confirm 记录某个分片已确认上传

这样即使页面刷新,重新初始化时也会从服务端获取真实的上传进度。比用localStorage靠谱多了,因为服务端才是最终权威的数据源。

注意处理并发上传的情况,服务端对upload-confirm接口要做好并发控制,避免重复记录。
点赞 1
2026-03-10 07:02