React 实战大文件分片上传:断点续传、并发切片与进度管控

test12026-10-161 次阅读

为什么需要分片上传

单文件直接 PUT 上传有几个现实问题:大文件占用一条连接太久、中途断网要从头再来、进度条只能粗粒度。把文件切成固定大小的片,逐片并发上传,就能并发利用带宽、断点续传只补缺失的片、进度按片累计。下面是一套可直接落地的实现。

切片与并发控制

先把文件按 5MB 切片,用一个带并发上限的上传池,避免一次性发起几百个请求把浏览器连接打满。

async function uploadFile(file: File, onProgress: (p: number) => void) {
  const CHUNK = 5 * 1024 * 1024;
  const total = Math.ceil(file.size / CHUNK);
  const uploaded = new Set<number>(); // 已成功的片,用于续传

  // 并发池:最多同时传 3 片
  const limit = 3;
  let cursor = 0;
  let done = 0;

  async function worker() {
    while (cursor < total) {
      const index = cursor++;
      const start = index * CHUNK;
      const blob = file.slice(start, start + CHUNK);
      const fd = new FormData();
      fd.append("index", String(index));
      fd.append("file", blob);
      fd.append("name", file.name);
      await fetch("/api/upload/chunk", { method: "POST", body: fd });
      uploaded.add(index);
      done++;
      onProgress(Math.round((done / total) * 100));
    }
  }

  await Promise.all(Array.from({ length: limit }, () => worker()));
}

断点续传

续传的关键是服务端能告诉前端"哪些片已经收过"。前端先问一次已上传的片索引,跳过它们即可。

async function resumeUpload(file: File) {
  const res = await fetch(`/api/upload/status?name=${encodeURIComponent(file.name)}`);
  const { uploaded }: { uploaded: number[] } = await res.json();
  const done = new Set(uploaded);
  let cursor = 0;
  while (done.has(cursor)) cursor++;
  // 其余逻辑与上面一致,但 worker 起点为 cursor
}

React 组件里怎么用

把上传逻辑收进一个组件,进度用 state 驱动进度条。注意用一个 ref 保存控制器,方便在用户点取消时中止。

function ChunkUploader() {
  const [progress, setProgress] = useState(0);
  const [busy, setBusy] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);

  async function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0];
    if (!file) return;
    setBusy(true);
    await uploadFile(file, setProgress);
    setBusy(false);
  }

  return (
    <div>
      <input ref={inputRef} type="file" onChange={handleChange} disabled={busy} />
      <progress value={progress} max={100} />
      <span>{progress}%</span>
    </div>
  );
}

工程注意点

  • 每片建议带 index 与文件唯一标识,服务端按 (name, index) 落盘,合并前校验片数。
  • 合并放到最后一片成功后触发,或者由前端显式调用 /api/upload/merge。
  • 并发数别太高,浏览器对同一域名的 HTTP/1.1 连接数有限,3 到 5 比较稳妥。
  • 给上传加整体超时与重试,单片失败重传该片即可,不要整文件重来。
T

test1

文章作者

为什么需要分片上传 单文件直接 PUT 上传有几个现实问题:大文件占用一条连接太久、中途断网要从头再来、进度条只能粗粒...

分类
技术
发布时间
2026-10-16
字数
约 2284 字
阅读
1 次