登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  前端

前端大文件上传怎么选:后端中转还是对象存储分片直传

来源: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 个;并发过高会同时占满浏览器连接、用户出口带宽和触发对象存储限流,进度条反而更容易频繁抖动。

服务端状态至少记录 fileKeyuploadId、文件大小、分片大小、已完成编号和创建时间。重试前先查询已完成分片,避免重复写入;用户点击取消时调用存储服务的取消接口,不能只把数据库状态改成“已取消”,否则会留下收费且无法被业务引用的孤儿分片。

前端对象存储分片上传中失败分片单独重试并在完整性校验后完成合并

完成合并前要核对三类证据

  1. 身份证据:服务端确认当前用户仍有权限,fileKeyuploadId 与创建记录一致,不能只相信浏览器回传的文件名。
  2. 分片证据:检查分片编号没有缺口,大小符合规则,ETag 或 checksum 与上传响应相符;最后一个分片可以小于其他分片。
  3. 业务证据:合并后的对象大小、媒体类型和文件指纹符合预期,扫描或转码成功后再把状态从 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 过期,或前端把字符串编号和服务端要求的整数混用了。先查完成请求的分片清单和对象存储返回的错误码。

什么时候应该保留后端中转?

需要同步内容审查、服务端加密、转码前置或内网存储时,后端中转更容易控制。可以先限制文件大小,再用流式处理降低内存和连接压力。

选择上传方案时,先画出数据流经的完整路径,再评估失败恢复逻辑和权限边界。大文件场景通常让浏览器直传分片、业务服务管理任务状态;对必须经过指定处理链路的文件,则保留中转并把大小、超时和回滚逻辑写进验收清单。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>