当前位置:首页 > 文章列表 > 文章 > 前端 > 前端上传大文件怎么用 Blob slice 实现可恢复分片

前端上传大文件怎么用 Blob slice 实现可恢复分片

来源:17golang原创 2026-09-07 20:35:47 0浏览 收藏

前端大文件上传不要把整个 File 一次塞进请求。更稳妥的做法是用 file.slice(start, end) 取出一个 Blob,为每个分片携带稳定的 uploadIdindex,上传失败只重试当前分片;页面刷新后再向服务端查询已收到的索引,跳过它们继续传。这样“可恢复”依赖的是服务端保存的分片状态,而不是浏览器里一个容易丢失的循环变量。

要点速览
  • Blob.slice(start, end) 的 end 是排除位置,适合按固定字节范围切片。
  • 恢复标识至少要稳定包含文件特征,服务端再把它映射到唯一 uploadId
  • 上传分片时不要手动设置 multipart/form-data 的 Content-Type,让浏览器生成 boundary。
  • 只有服务端确认所有 index 到齐,前端才请求合并;重复请求必须按 uploadId 与 index 幂等处理。

一、先固定恢复标识和接口契约

先把前后端约定写清楚。示例用文件名、大小和最后修改时间组成 resumeKey;生产环境可换成服务端签发的指纹或内容哈希,但原则不变:同一个文件再次选择时,初始化接口应返回同一个上传任务或明确告诉前端哪些分片已经存在。

接口前端发送服务端返回
POST /api/uploads/initname、size、lastModified、chunkSize、resumeKeyuploadId、chunkSize、received
POST /api/uploads/chunkuploadId、index、total、chunk、filenameindex、stored
POST /api/uploads/completeuploadId、totalmerged、url

received 是服务端已持久化的分片索引数组,例如 [0, 1, 3]。前端不应只保存“上次传到了第几个”,因为第 2 片可能失败,而第 3 片已经成功。

二、用 Blob.slice 切出单个分片

MDN 对 Blob.slice() 的定义是返回原 Blob 某个字节子集的新 Blob。它不会要求前端把整个文件转成一个大数组。下面的函数只负责计算范围并取片,end 不包含在结果中。

前端大文件上传中 File 经过 Blob slice 切成分片并由 FormData 携带 uploadId 与 index 的结构图
图1:浏览器只把当前 Blob 子集装进 FormData,上传请求携带分片索引和恢复标识。
function getChunk(file, index, chunkSize) {
  // end 是排除位置,最后一片不足 chunkSize 也能正常返回。
  const start = index * chunkSize;
  const end = Math.min(start + chunkSize, file.size);
  return file.slice(start, end);
}

function getTotalChunks(file, chunkSize) {
  // 向上取整,保证尾部不足一片时仍有一个合法 index。
  return Math.ceil(file.size / chunkSize);
}

例如 chunkSize 设为 8 MiB,前端内存里同时只需要处理当前分片。这里不绑定某个固定大小,实际项目应结合网关限制、网络质量和服务端磁盘写入能力压测后再定。

三、给单片请求加幂等重试

重试对象应是当前 index,而不是从头重传整个文件。服务端用 uploadId + index 作为幂等键:同一分片重复到达时覆盖或直接返回已保存状态,但不能生成第二份分片。

async function uploadChunk(file, upload, index, maxRetry = 3) {
  const chunk = getChunk(file, index, upload.chunkSize);
  let lastError;

  for (let attempt = 0; attempt  setTimeout(resolve, 500 * 2 ** attempt));
    }
  }
  throw lastError;
}

这段代码没有把 Content-Type 写死,是因为 FormData 的 multipart boundary 需要由浏览器生成。接口返回 4xx 时应先修正参数或任务状态,不要无条件重试;只有超时、断网等可恢复错误适合进入退避循环。

四、刷新后跳过已完成分片

初始化接口需要根据 resumeKey 返回 uploadIdreceived。前端将数组转成 Set,每次循环只提交缺失的索引。真正的断点续传因此由服务端状态驱动。

可恢复分片上传中 uploadId 关联 received 分片集合并在缺失分片齐全后进入 merge 的结构图
图2:恢复的关键不是记住循环位置,而是由 uploadId 对应服务端的 received 分片集合。
async function uploadFile(file) {
  const chunkSize = 8 * 1024 * 1024;
  const total = getTotalChunks(file, chunkSize);
  const resumeKey = [file.name, file.size, file.lastModified].join(":");

  const initResponse = await fetch("/api/uploads/init", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      name: file.name,
      size: file.size,
      lastModified: file.lastModified,
      chunkSize,
      resumeKey,
    }),
  });
  if (!initResponse.ok) throw new Error("无法初始化上传任务");
  const upload = { ...(await initResponse.json()), total, chunkSize };
  const received = new Set(upload.received || []);

  for (let index = 0; index 

示例用串行上传换取简单的顺序和较低的并发压力。若后续要并发,仍要保留同一个 uploadIdindex 幂等约束,并限制并发数;不要把“并发”误当成“可恢复”的实现本身。

五、全部到齐后再请求合并

合并接口必须由服务端再次校验分片数量和索引连续性,不能只相信前端循环已经结束。前端只在所有缺失索引都上传成功后调用它:

async function finishUpload(uploadId, total) {
  // 服务端按 uploadId 检查 0 到 total - 1,再按序合并临时分片。
  const response = await fetch("/api/uploads/complete", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ uploadId, total }),
  });
  if (!response.ok) throw new Error("分片未齐全,暂不合并");
  return response.json();
}

验收时重点看三种场景:中途断网后只补传缺失 index;重复点击继续上传不会产生重复文件;最后一片小于 chunkSize 时也能正常合并。只要这三项成立,Blob.slice、重试、恢复标识和合并链路就形成了闭环。

常见问题

为什么不能只把当前 index 存到 localStorage?

因为分片可能乱序完成,单个数字无法表达缺口。应以服务端的 received 集合为准,必要时再把 uploadId 缓存在本地作为查询入口。

每次重试都要重新调用 slice 吗?

可以重新调用,范围计算是确定的;也可以复用当前 Blob。关键是请求仍携带同一 uploadId 和 index,并让服务端幂等。

能否手动设置 multipart/form-data?

不要这样做。使用 FormData 时让浏览器生成带 boundary 的 Content-Type,否则服务端可能无法正确解析字段和文件。

文件名相同就能恢复吗?

不能。文件名可能相同但内容不同,至少还要结合大小和修改时间;对重要文件可改为服务端指纹或内容哈希,并在初始化接口中重新确认。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 命令行 flag 参数缺失时怎么返回可读帮助而不是 panicGo 命令行 flag 参数缺失时怎么返回可读帮助而不是 panic
上一篇
Go 命令行 flag 参数缺失时怎么返回可读帮助而不是 panic
Go io/fs 怎么用统一接口读取磁盘和嵌入文件
下一篇
Go io/fs 怎么用统一接口读取磁盘和嵌入文件
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    173次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    104次使用
  • AI Prompt Library:免费AI提示词库,助力ChatGPT高效创作与营销
    AI Prompt Library
    探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
    31次使用
  • LangGPT提示词框架:结构化Prompt设计方法与开源工具指南
    LangGPT
    LangGPT是一种受编程语言启发的结构化提示词设计工具,提供双层框架、模块化模板及变量功能,帮助用户高效编写高质量Prompt。该项目已在GitHub免费开源,适用于内容创作、编程辅助等多场景。
    41次使用
  • ClickPrompt:AI提示词生成与优化工具,支持Stable Diffusion、ChatGPT及代码辅助
    ClickPrompt
    ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
    77次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码