前端大文件上传怎么选:后端中转还是对象存储分片直传
来源:17golang原创
时间:2026-08-11 16:24:35 448浏览 收藏
一个 8GB 的视频从浏览器上传时,真正决定体验的不是“上传按钮”长什么样,而是数据流经的路径:如果所有数据先进入业务服务,应用服务器、网关和出口带宽会一起承压;如果浏览器拿到限时上传地址后把分片直接交给对象存储,业务服务只负责授权、记录和合并确认。两种方案都能跑通,差异集中在流量成本、失败恢复能力和权限可控范围上。
要点速览
- 小文件或必须经过业务审查的内容适合后端中转,链路短、规则集中但会占用应用带宽。
- 大文件优先考虑对象存储分片直传,业务服务只下发 uploadId、partNumber 对应的限时 URL。
- 浏览器用
Blob.slice()切片,失败只重传单个 part;不要因为一个分片失败而重传整个文件。 - 完成上传前要核对文件指纹、分片编号、ETag 或 checksum,取消任务时清理未完成的 multipart upload。
先看数据到底经过哪几台机器
后端中转的路径是“浏览器 → 网关 → 业务服务 → 对象存储”。它实现逻辑最直观,服务端可以在写入前做用户权限、文件类型和内容扫描,但同一份字节至少穿过业务集群一次,网关超时、连接数和出口带宽都要按文件大小提前估算余量。
分片直传的路径是“浏览器 → 业务服务拿授权 → 浏览器 → 对象存储”。业务服务不搬运文件正文,只保存文件名、大小、指纹、对象键和上传状态。预签名 URL 必须绑定具体对象和 HTTP 方法,并设置较短过期时间;它是给到单次操作的临时能力,不是把存储桶全权限交给浏览器。

按文件大小和业务约束做选择
| 判断维度 | 后端中转 | 分片直传 |
|---|---|---|
| 文件体量 | 几MB到几十MB更简单 | 数百MB、GB级更合适 |
| 业务审查 | 服务端可在落盘前统一检查 | 上传后异步扫描,再决定是否可见 |
| 失败恢复 | 通常重试整次请求 | 按 partNumber 重传失败分片 |
| 服务成本 | 消耗应用带宽和连接 | 应用主要承受授权与状态请求 |
如果文件必须经过服务端转码、加密或脱敏,中转并不一定是差方案;可以把它限制在受控的内部网络,配合流式写入和大小上限管控。对于用户上传的大视频、安装包、训练数据,直传通常更容易把应用服务从“数据搬运工”切换为“流程协调者”。
浏览器分片只做数据切割,不负责权限
浏览器端可以用 Blob.slice(start, end) 取得一段连续数据。切片大小应由对象存储限制、网络状况和内存占用共同决定;实践中先从 8MB 或 16MB 起步,再依据失败率和并发数调整。关键是不要把完整文件一次性读进内存。
const partSize = 16 * 1024 * 1024;
const parts = [];
for (let start = 0, partNumber = 1; start {
const { url } = await getUploadUrl({ fileKey, uploadId, partNumber });
const response = await fetch(url, { method: "PUT", body });
if (!response.ok) throw new Error(`part ${partNumber} failed: ${response.status}`);
return { partNumber, etag: response.headers.get("ETag") };
};
这里的 getUploadUrl 只返回当前分片需要的地址,不能让浏览器自行拼接存储服务签名。生产环境还要处理跨域响应头,至少明确允许读取 ETag 或 checksum 的响应头,否则浏览器虽然上传成功,前端却拿不到完成合并所需的校验信息。
失败时只重试分片,并把并发设成可控变量
分片上传的价值在网络不稳定场景下最明显:第 17 片超时,只需重新请求第 17 片,不影响已经成功的其他分片。前端可以维护一个待上传队列,把并发控制在 3 到 6 个;并发过高会同时占满浏览器连接、用户出口带宽和触发对象存储限流,进度条反而更容易频繁抖动。
服务端状态至少记录 fileKey、uploadId、文件大小、分片大小、已完成编号和创建时间。重试前先查询已完成分片,避免重复写入;用户点击取消时调用存储服务的取消接口,不能只把数据库状态改成“已取消”,否则会留下收费且无法被业务引用的孤儿分片。

完成合并前要核对三类证据
- 身份证据:服务端确认当前用户仍有权限,
fileKey、uploadId与创建记录一致,不能只相信浏览器回传的文件名。 - 分片证据:检查分片编号没有缺口,大小符合规则,ETag 或 checksum 与上传响应相符;最后一个分片可以小于其他分片。
- 业务证据:合并后的对象大小、媒体类型和文件指纹符合预期,扫描或转码成功后再把状态从
UPLOADING改为READY。
文件指纹可以用“大小 + 修改时间 + 分段摘要”做快速去重,也可以在服务端对最终对象做更严格的哈希校验。不要把浏览器计算出来的 MD5 当成唯一安全依据,它更适合帮助恢复上传和发现明显的内容变化。
一份可落地的接口边界
POST /uploads/init
{ "name": "demo.mp4", "size": 8589934592, "fingerprint": "..." }
// response: fileKey, uploadId, partSize, completedParts[]
GET /uploads/{fileKey}/parts/{partNumber}/url
POST /uploads/{fileKey}/complete
{ "uploadId": "...", "parts": [{ "partNumber": 1, "etag": "..." }] }
POST /uploads/{fileKey}/abort
complete 接口不应直接接受一个“上传成功”布尔值,而要依据存储服务返回的合并结果更新状态。接口还要具备幂等性:同一个 fileKey + uploadId 重复点击完成时,返回已有结果或明确的状态,不要再次创建一份对象。
常见问题:直传方案的边界在哪里
小文件也要上分片直传吗?
不必。小文件的分片状态、签名和回收逻辑可能比文件本身更复杂,先用单次上传或后端中转更省维护成本。
预签名 URL 泄露后会不会拿到整个存储桶?
正常配置下不会。URL 应绑定单个对象、单个方法和短过期时间;同时限制对象键生成规则,并在服务端记录签发者与上传任务。
为什么上传成功了却无法完成合并?
常见原因是分片编号缺失、ETag 没有暴露给浏览器、uploadId 过期,或前端把字符串编号和服务端要求的整数混用了。先查完成请求的分片清单和对象存储返回的错误码。
什么时候应该保留后端中转?
需要同步内容审查、服务端加密、转码前置或内网存储时,后端中转更容易控制。可以先限制文件大小,再用流式处理降低内存和连接压力。
选择上传方案时,先画出数据流经的完整路径,再评估失败恢复逻辑和权限边界。大文件场景通常让浏览器直传分片、业务服务管理任务状态;对必须经过指定处理链路的文件,则保留中转并把大小、超时和回滚逻辑写进验收清单。
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习