蓝戒博客

  • 首页
  • 研发说
  • 架构论
  • 效能录
  • AI谈
  • 随笔集
智构苍穹
融合 AI、架构与工程实践,沉淀方法论,构建可持续的技术价值。
  1. 首页
  2. 研发说
  3. 正文

吃透大文件分片上传:原理、方案与最佳实践

2026年1月12日 58点热度 0人点赞 0条评论

在企业级 Web 应用中,大文件上传几乎是一个绕不开的话题:视频、日志包、模型文件、设计稿、数据集……
当文件体积从几十 MB 上升到 GB 级别,传统的上传方式往往会彻底失效。

本文将系统讲清楚:

  • 大文件上传会遇到哪些核心问题
  • 分片上传的整体设计思路
  • 秒传 / 断点续传 / 并发控制如何实现
  • 前后端核心实现代码
  • 线上落地的最佳实践

一、为什么普通上传在大文件面前会失败?

1. 请求不稳定,失败率高

  • HTTP 请求时间过长
  • 网络抖动、代理超时、浏览器中断
  • 任意一次失败都要 重头再来

2. 用户体验极差

  • 上传过程中页面刷新或关闭 → 全部丢失
  • 无法展示准确进度
  • 失败后不可恢复

3. 服务端压力巨大

  • 单请求占用连接时间过长
  • 内存 / 带宽被长时间占用
  • 高并发下容易拖垮服务

二、大文件上传的核心解决思路

核心思想:分而治之

把一个大文件,拆成多个可控的小文件,分别上传,最后在服务端合并。

标准分片上传流程

选择文件
   ↓
文件切片(chunk)
   ↓
并发上传分片
   ↓
记录已上传分片
   ↓
全部完成后通知服务端合并

三、一个成熟的分片上传方案应具备哪些能力?

能力说明
分片上传将大文件拆分为多个 chunk
断点续传已上传的分片不再重复上传
秒传文件已存在时直接完成
并发控制控制同时上传的分片数量
失败重试单分片失败可重试
服务端合并确保合并顺序和完整性

四、整体架构设计

前后端职责划分

前端负责:

  • 文件切片
  • 计算文件唯一标识(hash)
  • 分片并发上传
  • 失败重试
  • 上传进度控制

后端负责:

  • 校验文件是否已存在(秒传)
  • 记录已上传分片
  • 接收分片并持久化
  • 按顺序合并文件
  • 清理临时分片

五、前端核心实现(浏览器)

1️⃣ 文件切片

TypeScript
/**
 * 将文件切分为固定大小的分片
 */
function createFileChunks(file: File, chunkSize = 5 * 1024 * 1024) {
  const chunks: Blob[] = [];
  let start = 0;

  while (start < file.size) {
    chunks.push(file.slice(start, start + chunkSize));
    start += chunkSize;
  }

  return chunks;
}

2️⃣ 计算文件 Hash(用于秒传 & 断点续传)

TypeScript
/**
 * 计算文件 hash(基于 Web Worker + SparkMD5 更优)
 */
async function calculateHash(file: File): Promise<string> {
  const buffer = await file.arrayBuffer();
  const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
  return Array.from(new Uint8Array(hashBuffer))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
}

3️⃣ 上传单个分片

TypeScript
/**
 * 上传单个分片
 */
function uploadChunk(
  chunk: Blob,
  index: number,
  hash: string
) {
  const formData = new FormData();
  formData.append('chunk', chunk);
  formData.append('index', index.toString());
  formData.append('hash', hash);

  return fetch('/upload/chunk', {
    method: 'POST',
    body: formData,
  });
}

4️⃣ 并发上传控制(重点)

TypeScript
/**
 * 控制并发上传
 */
async function uploadChunksWithLimit(
  tasks: (() => Promise<any>)[],
  limit = 4
) {
  const executing: Promise<any>[] = [];

  for (const task of tasks) {
    const p = task();
    executing.push(p);

    if (executing.length >= limit) {
      await Promise.race(executing);
      executing.splice(
        executing.findIndex(e => e === p),
        1
      );
    }
  }

  return Promise.all(executing);
}

5️⃣ 合并请求

TypeScript
/**
 * 通知服务端合并分片
 */
function mergeChunks(hash: string, fileName: string) {
  return fetch('/upload/merge', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ hash, fileName }),
  });
}

六、服务端核心实现(Node.js 示例)

1️⃣ 接收分片

TypeScript
/**
 * 接收分片并保存到临时目录
 */
app.post('/upload/chunk', upload.single('chunk'), (req, res) => {
  const { hash, index } = req.body;
  const chunkDir = path.resolve(__dirname, 'temp', hash);

  if (!fs.existsSync(chunkDir)) {
    fs.mkdirSync(chunkDir, { recursive: true });
  }

  fs.renameSync(
    req.file.path,
    path.join(chunkDir, index)
  );

  res.json({ success: true });
});

2️⃣ 合并分片

TypeScript
/**
 * 合并所有分片
 */
app.post('/upload/merge', async (req, res) => {
  const { hash, fileName } = req.body;
  const chunkDir = path.resolve(__dirname, 'temp', hash);
  const filePath = path.resolve(__dirname, 'uploads', fileName);

  const chunks = fs.readdirSync(chunkDir).sort((a, b) => Number(a) - Number(b));
  const writeStream = fs.createWriteStream(filePath);

  for (const chunk of chunks) {
    const chunkPath = path.join(chunkDir, chunk);
    writeStream.write(fs.readFileSync(chunkPath));
    fs.unlinkSync(chunkPath);
  }

  writeStream.end();
  fs.rmdirSync(chunkDir);

  res.json({ success: true });
});

七、断点续传与秒传如何实现?

关键接口:查询已上传分片

TypeScript
GET /upload/status?hash=xxx

返回:

JSON
{
  "uploadedChunks": [0,1,2,5,6]
}

前端只上传 缺失的分片,即可实现:

  • 断点续传
  • 页面刷新继续
  • 网络失败恢复

八、最佳实践总结(非常重要)

前端最佳实践

  • 分片大小:2MB ~ 10MB
  • 并发数:3 ~ 6
  • hash 计算使用 Web Worker
  • 上传失败要支持重试
  • 显示真实进度(已完成分片 / 总分片)

后端最佳实践

  • 分片目录按 hash 隔离
  • 合并采用流式写入
  • 合并完成立即清理临时文件
  • 对合并接口加锁,防止重复合并
  • 支持幂等处理

九、总结

大文件分片上传并不是一个“技巧”,而是一个系统性工程问题:

  • 前端:性能、并发、稳定性、体验
  • 后端:可靠性、幂等性、资源控制
  • 架构:可恢复、可扩展、可维护

一套设计良好的分片上传方案,能够将 GB 级文件上传成功率提升到接近 100%,并显著改善用户体验。

标签: 分片上传 大文件上传 并发上传 断点续传
最后更新:2026年1月12日

cywcd

我始终相信,技术不仅是解决问题的工具,更是推动思维进化和创造价值的方式。从研发到架构,追求极致效能;在随笔中沉淀思考,于 AI 中对话未来。

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

cywcd

我始终相信,技术不仅是解决问题的工具,更是推动思维进化和创造价值的方式。从研发到架构,追求极致效能;在随笔中沉淀思考,于 AI 中对话未来。

最新 热点 随机
最新 热点 随机
前端开发 TanStack 化:从“框架思维”到“能力组合”的工程演进 Docker 进阶(七):容器化体系设计总结与生产落地经验复盘 Docker 进阶(六):生产环境中的 Docker 安全、监控与日志体系 Docker 进阶(五):Docker + CI/CD —— 从代码提交到自动部署 Docker 进阶(四):使用 Docker Swarm 与 Kubernetes 实现容器编排 Docker 进阶(三):深入理解镜像层(Layer)与缓存机制
Workbox:可直接用于生产环境的 Service Worker 利器快速构建项目文档网站:主流文档站点工具选型与对比DiceBear:30+ 风格、完全可定制的开源头像生成解决方案ES2015 → ES2025:JavaScript 十年演进全景回顾与核心 API 总结ECMAScript 2025(ES16)深度解析20 个现代 JavaScript API 深度盘点
InversifyJS:让 TypeScript 项目真正“解耦”的依赖注入利器 探索angularJS中的$watch,$apply,$digest理解双向数据绑定 移动web开发头部meta标签5种实用方法 Hybrid 开发全攻略:从原理到实战 CrewAI:基于角色协作的 AI Agent 团队框架浅析 Code Inspector:页面开发提效的神器
最近评论
渔夫 发布于 3 个月前(11月05日) 学到了,感谢博主分享
沙拉小王子 发布于 8 年前(11月30日) 适合vue入门者学习,赞一个
沙拉小王子 发布于 8 年前(11月30日) 适合vue入门者学习,赞一个
cywcd 发布于 9 年前(04月27日) 请参考一下这篇文章http://www.jianshu.com/p/fa4460e75cd8
cywcd 发布于 9 年前(04月27日) 请参考一下这篇文章http://www.jianshu.com/p/fa4460e75cd8

COPYRIGHT © 2025 蓝戒博客_智构苍穹-专注于大前端领域技术生态. ALL RIGHTS RESERVED.

京ICP备12026697号-2