You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReactJS实现DropBox式断点续传:localStorage状态存取问题

断点续传功能修复方案(React+Express)

核心问题分析

当前代码在断点续传实现上的关键问题:

  • localStorage仅存储全局uploadedChunks数组,未关联具体文件标识,无法区分不同文件的上传进度
  • 页面加载时的状态恢复逻辑未匹配当前文件列表,直接读取的全局状态无法对应到单个文件
  • 文件上传完成后未清理对应localStorage记录,导致同名文件上传时出现错误

修复步骤与代码修改

1. 重构localStorage存储结构,按文件唯一标识存储

用文件名+文件大小作为唯一标识,存储每个文件的已上传块列表和总块数,避免同名文件冲突:

// 在uploadChunk函数中修改存储逻辑
function uploadChunk(readerEvent) {
  const file = files[currentFileIndex];
  const data = readerEvent.target.result;
  const fileSize = file.size;
  const isLastChunk = currentChunkIndex === Math.ceil(fileSize / chunkSize) - 1;

  // 生成文件唯一标识
  const fileKey = `${file.name}-${fileSize}`;
  // 更新当前文件的已上传块
  const updatedChunks = [...new Set([...(fileUploadStates[fileKey]?.uploadedChunks || []), currentChunkIndex])];
  
  // 构建当前文件的上传状态
  const fileState = {
    uploadedChunks: updatedChunks,
    totalChunks: Math.ceil(fileSize / chunkSize)
  };
  // 合并已有状态,避免覆盖其他文件记录
  const existingState = JSON.parse(localStorage.getItem("dropboxUploaderState")) || {};
  const newState = {
    ...existingState,
    lastUploadedFileIndex: currentFileIndex,
    [fileKey]: fileState
  };
  localStorage.setItem("dropboxUploaderState", JSON.stringify(newState));
  setFileUploadStates(newState);

  // 原有上传逻辑...
}

2. 修复页面加载时的状态恢复逻辑

匹配当前文件列表与localStorage存储的状态,恢复对应文件的上传进度:

// 替换原有的恢复useEffect
useEffect(() => {
  const storedState = localStorage.getItem("dropboxUploaderState");
  if (storedState && files.length > 0) {
    const parsedState = JSON.parse(storedState);
    let targetFileIndex = null;
    let targetChunkIndex = null;

    files.forEach((file, index) => {
      const fileKey = `${file.name}-${file.size}`;
      const fileState = parsedState[fileKey];
      if (!fileState) return;

      // 文件已完成上传,标记状态
      if (fileState.uploadedChunks.length === fileState.totalChunks) {
        file.finalFilename = parsedState.finalFilename || file.name;
        setLastUploadedFileIndex(index);
      } else {
        // 未完成,定位到最后一个已上传块的下一个
        targetFileIndex = index;
        targetChunkIndex = fileState.uploadedChunks[fileState.uploadedChunks.length - 1] + 1;
      }
    });

    if (targetFileIndex !== null) {
      setCurrentFileIndex(targetFileIndex);
      setCurrentChunkIndex(targetChunkIndex);
    }
  }
}, [files]);

3. 维护单文件的已上传块校验

将全局uploadedChunks改为对象,存储每个文件的上传状态:

// 替换原有的uploadedChunks状态
const [fileUploadStates, setFileUploadStates] = useState({});

// 修改readAndUploadCurrentChunk中的校验逻辑
function readAndUploadCurrentChunk() {
  const file = files[currentFileIndex];
  if (!file) return;

  const fileKey = `${file.name}-${file.size}`;
  const currentFileState = fileUploadStates[fileKey] || { uploadedChunks: [] };
  const from = currentChunkIndex * chunkSize;
  const to = from + chunkSize;
  const blob = file.slice(from, to);

  // 跳过已上传的块
  if (currentFileState.uploadedChunks.includes(currentChunkIndex)) {
    setCurrentChunkIndex(currentChunkIndex + 1);
    return;
  }

  const reader = new FileReader();
  reader.onload = (e) => uploadChunk(e);
  reader.readAsDataURL(blob);
}

4. 上传完成后清理localStorage记录

文件全部块上传完成后,移除对应文件的存储记录:

// 在uploadChunk函数的isLastChunk分支中添加
if (isLastChunk) {
  file.finalFilename = response.data.finalFilename;
  setLastUploadedFileIndex(currentFileIndex);
  setCurrentChunkIndex(null);
  
  // 清理当前文件的localStorage记录
  const fileKey = `${file.name}-${file.size}`;
  const existingState = JSON.parse(localStorage.getItem("dropboxUploaderState")) || {};
  delete existingState[fileKey];
  localStorage.setItem("dropboxUploaderState", JSON.stringify(existingState));
}

内容的提问来源于stack exchange,提问作者Rohit W3Dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 13:32:02