导读:本期聚焦于林小满创作的《React如何实现大文件断点续传?分片上传与暂停继续功能完整开发指南》,敬请观看详情。上传一个大文件到一半网络断开,只能从头再来,这种体验对用户非常不友好。断点续传技术通过将文件切分成多个小块逐个上传,即使中途失败也能从断开的位置继续,大幅提升上传成功率。本文围绕React技术栈,详细讲解分片上传的核心原理,包括利用Blob.prototype.slice进行文件切片、计算文件hash实现秒传与续传判断、并发控制上传队列的封装,以及暂停、继续、取消等交互功能的完整实现思路,同时给出可直接复用的代码示例,帮助你在项目中快速落地一套稳定的大文件上传方案。

在网盘、视频平台、医疗影像系统等场景中,动辄几百MB甚至几个GB的文件上传几乎是绕不开的需求。如果采用传统的整文件一次性上传,一旦网络波动导致请求失败,整个文件就得重新来过,用户体验极差。断点续传的思路是把大文件切成若干个小分片,逐个上传,每个分片上传成功后由服务端记录下来,这样即使中途断网或用户主动暂停,下次也能从上次中断的位置继续,而不必重头开始。本文将以React为前端技术栈,完整实现一套包含分片上传、进度展示、暂停与继续功能的方案。

React如何实现大文件断点续传?分片上传与暂停继续功能完整开发指南

分片上传的核心原理与文件切片

分片上传的第一步是把文件对象切成小块。浏览器提供的Blob.prototype.slice方法可以很方便地从一个File或Blob对象中截取指定范围的数据,返回的仍然是Blob对象,可以直接作为请求体发送。切片时需要确定一个合适的分片大小,通常建议设置为2MB到10MB之间:分片太小会导致请求次数过多、HTTP头部开销占比增大;分片太大则失败重传的代价变高,也失去了断点续传的精细粒度。

下面是基础的切片逻辑,将文件按照固定大小切割并生成带编号的分片数组:

const CHUNK_SIZE = 5 * 1024 * 1024; // 每个分片 5MB

function createFileChunks(file) {
  const chunks = [];
  let cur = 0;
  let index = 0;
  while (cur < file.size) {
    chunks.push({
      chunk: file.slice(cur, cur + CHUNK_SIZE),
      index: index++,
    });
    cur += CHUNK_SIZE;
  }
  return chunks;
}

切片完成后,每个分片需要携带文件标识、分片序号、分片总数等信息,服务端才能正确地把它们重新拼装起来。文件标识不能简单使用文件名,因为不同用户可能上传同名但内容不同的文件,稳妥的做法是对文件内容计算hash。可以使用spark-md5这个库,它基于增量计算,可以边读取分片边更新hash值,避免一次性把整个大文件读入内存导致页面卡死。如果文件特别大,还应该把hash计算放到Web Worker中执行,保证主线程的流畅。

秒传与续传判断:上传前的接口设计

在真正开始上传之前,前端需要先调用一个校验接口,把文件的hash值发给服务端。服务端根据这个hash检查两件事:一是是否已经存在完整文件,如果存在,说明该文件之前被上传过,直接返回成功,前端根本不需要传输任何数据,这就是所谓的秒传;二是如果完整文件不存在,返回已经上传成功的分片编号列表,前端拿到这个列表后,把对应的分片过滤掉,只上传缺失的部分,这就实现了续传。

前端请求校验接口的示例代码如下:

async function verifyUpload(fileName, fileHash) {
  const res = await fetch('/api/upload/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ fileName, fileHash }),
  });
  const data = await res.json();
  return data; // { shouldUpload: true, uploadedChunks: [0, 2, 3] }
}

这个接口的返回结构很简单,shouldUpload表示是否需要上传,uploadedChunks是服务端已存在的分片索引数组。如果shouldUpload为false,前端直接把进度置为百分之百即可。这种设计的价值在于,无论用户是刷新页面、关闭浏览器还是切换网络,只要文件hash不变,上传进度就相当于被永久保存在服务端了,这比把进度存在localStorage里可靠得多。

并发控制与暂停继续功能的实现

如果分片数量很多,一次性把所有请求发出去会瞬间打满浏览器对同一域名的并发连接数限制(HTTP/1.1下通常是6个),还会给服务端带来瞬时压力。所以需要一个并发控制模块,维护一个请求池,始终保持固定数量的请求在跑,某个请求完成后自动从队列中取出下一个补充进来。

更关键的是暂停与继续。实现思路是维护一个可变的控制状态,比如用一个paused标志位配合AbortController。每个分片请求都绑定一个独立的AbortController,当用户点击暂停时,把当前正在进行的请求全部abort掉,并停止从队列中取新任务;点击继续时,重新校验一遍服务端已上传的分片,再把剩余分片重新加入队列执行。下面的React Hook完整展示了这套逻辑:

import { useRef, useState, useCallback } from 'react';

export function useChunkUpload() {
  const [progress, setProgress] = useState(0);
  const controllersRef = useRef([]);
  const pausedRef = useRef(false);

  const uploadChunks = useCallback(async (chunks, fileHash, uploadedSet) => {
    const list = chunks.filter(c => !uploadedSet.has(c.index));
    let done = uploadedSet.size;

    const runOne = async (chunk) => {
      if (pausedRef.current) return;
      const controller = new AbortController();
      controllersRef.current.push(controller);
      const form = new FormData();
      form.append('chunk', chunk.chunk);
      form.append('index', chunk.index);
      form.append('fileHash', fileHash);
      await fetch('/api/upload/chunk', {
        method: 'POST',
        body: form,
        signal: controller.signal,
      });
      done += 1;
      setProgress(Math.round((done / chunks.length) * 100));
    };

    // 简单的并发池实现,最多同时4个请求
    const pool = [];
    const taskQueue = [...list];
    while (taskQueue.length || pool.length) {
      while (pool.length < 4 && taskQueue.length) {
        const task = runOne(taskQueue.shift())
          .finally(() => pool.splice(pool.indexOf(task), 1));
        pool.push(task);
      }
      await Promise.race(pool);
    }
  }, []);

  const pause = useCallback(() => {
    pausedRef.current = true;
    controllersRef.current.forEach(c => c.abort());
    controllersRef.current = [];
  }, []);

  const resume = useCallback(() => {
    pausedRef.current = false;
  }, []);

  return { progress, uploadChunks, pause, resume };
}

注意几个细节:进度条的计算要基于服务端已确认的分片数,而不是本地发起的请求数,否则暂停后再继续,进度会出现回退或跳变。abort被触发的请求会抛出AbortError,在runOne里可以捕获这个异常并静默处理,避免它污染并发池的race逻辑。另外,暂停后恢复时强烈建议重新调用verify接口,因为暂停期间可能有其他端在同一账号下传过同一个文件,重新校验可以避免重复上传已经存在的分片。

最后别忘了通知合并。所有分片上传完成后,前端再调用一个merge接口,把文件hash、文件名和分片大小告诉服务端,由服务端按序号把分片合并成完整文件。合并动作放在服务端做而不是前端,是因为前端无法直接操作服务器磁盘上的分片文件。至此整个断点续传闭环就完成了:切片、hash计算、秒传校验、并发上传、暂停继续、合并通知,每一步职责清晰,稍加改造就能直接应用到生产项目中。

React断点续传分片上传文件上传修改时间:2026-09-07 21:22:43

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260907/52454.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。