Appearance
文件与媒体上传管线:直传 OSS、压缩与分片断点续传
回到总览:后台 / API / 数据层
相关模块:大文件下载、断点续传与多线程分片下载架构 · 项目案例:download_service.dart的下载 / 校验 / 原子更新 / 失败恢复
一句话定义
文件与媒体上传管线是指在移动端处理图片、音视频等大文件上传时,通过客户端本地预压缩、获取服务端 OSS / COS 预签名凭证直传(Presigned URL) 以及大文件分片并发上传与断点续传,实现高可靠、低服务器带宽消耗的媒体传输架构。
代码索引
计划补齐的实验:
labs/shared/backend-data/media-pipeline-demo/— 图片/视频质量压缩、OSS 预签名直传与 MultipartUpload 模拟测试
为什么需要
- 为什么移动端上传图片或视频时,绝对不能将大文件字节流直接 MultipartPOST 提交给业务后台服务器?
- 一句话答:业务后台服务器(如 Java / Node API)的带宽和 CPU 资源极其昂贵,处理海量大文件流会导致 API 服务器网关卡死;正确的架构是向后台申请对象存储(阿里云 OSS / 腾讯云 COS / AWS S3)的预签名直传 URL,由客户端直接将文件流上传到对象存储 CDN 节点。
- 为什么 10 年 Android 工程师设计媒体上传管线时必须引入预压缩(Pre-compression)?
- 一句话答:现代手机拍照单张图片高达 10MB~20MB,未压缩直接上传不仅消耗用户大量移动流量,且在弱网下极易失败;本地压缩(如将图片降至 1080P、80% 质量)可以将体积缩小 90% 以上,显著提升上传成功率。
底层机制
1. OSS 预签名凭证直传流程 (Presigned URL Upload)
text
[客户端 (Android / Flutter)] ─── (1. POST /api/upload/credential) ───► [业务 API 服务器]
│ │
│ ◄── (2. 返回预签名 URL: https://bucket.oss...&Signature=xxx) ──┘
│
└─── (3. PUT 预签名 URL 直接上传大文件二进制流) ─────────────► [阿里云 OSS / AWS S3]
│
▼
[业务 API 服务器] ◄─── (4. OSS 成功回调 Webhook / 客户端确认) ──────────────┘- 申请凭证:客户端携带文件名、MD5 向业务服务器发起申请。
- 生成签名:业务服务器使用 AccessKeySecret 生成带有有效期的预签名 URL(Presigned URL) 。
- 直传 OSS:客户端直接使用 HTTP
PUT将文件流上传到 OSS 节点,彻底不占用业务服务器带宽!
2. 大文件分片上传 (Multipart Upload) 与断点续传
对于大于 20MB 的视频文件,采用分片上传:
text
大视频文件 (100MB)
├── Part 1 (10MB) ──► 线程 1 上传到 OSS ──► 获得 ETag 1
├── Part 2 (10MB) ──► 线程 2 上传到 OSS ──► 获得 ETag 2
└── ...
│
▼
[合并分片] ── CompleteMultipartUpload (带上所有 ETag) ──► 文件合成完成!Android / Flutter / Web / Backend 对照
| 环节 | Android 宿主 | Flutter (Dart) | Web 前端 |
|---|---|---|---|
| 图片压缩 | Luban (鲁班压缩) / Native WebP | flutter_image_compress | Canvas 压缩 (toDataURL) |
| 视频压缩 | MediaCodec / FFmpeg / LightCompressor | video_compress 插件 | WebAssembly (FFmpeg.wasm) |
| 直传引擎 | OkHttp / 阿里云 OSS Android SDK | dio / OSS Dart SDK | axios / OSS JS SDK |
常见场景
1. 图片上传前动态压缩处理 (Luban 算法口径)
java
// 使用 Luban 算法根据原始宽高比例计算最优压缩分辨率
File compressedImageFile = Luban.with(context)
.load(originalFile)
.ignoreBy(100) // 100KB 以下不压缩
.setTargetDir(getCacheDir().getPath())
.get(originalFile.getAbsolutePath());常见误配、事故后果与排障
1. 事故:客户端在后台上传大视频时遭遇进程被杀,恢复后从头开始上传
- 误配原因:未将大视频切割为固定 Part(如 5MB 一个分片),没有在本地数据库记录已被 OSS 确认接收的 PartNumber 和 ETag。
- 后果:90% 的上传进度因断网或后台被杀作废,重新上传消耗数几百兆用户流量。
- 排障与修法:使用 OSS 分片上传 API (
InitiateMultipartUpload+UploadPart);每成功上传一个 Part,在本地记录其ETag,恢复上传时调用ListParts跳过已上传的分片。
与相近概念对比
| 上传方式 | 业务服务器带宽消耗 | 是否支持分片断点续传 | 适合场景 |
|---|---|---|---|
| 业务 API 中转 | 极高 | 否 | 极小文件(如用户头像修改 50KB) |
| OSS 预签名直传 | 零 | 否 | 普通图片、小音频直传 |
| OSS 分片直传 | 零 | 是 | 大视频、日志压缩包、大资源包 |
对应实验
计划补齐的实验:
labs/shared/backend-data/media-pipeline-demo/— 图片/视频质量压缩、OSS 预签名直传与 MultipartUpload 模拟测试
复习检查题
为什么在上传大文件时,推荐使用“OSS 预签名直传(Presigned URL)”而不是将文件直接传给业务后台服务器?
答:因为业务后台服务器的主要职责是处理轻量级的 JSON 业务逻辑,其网关和网络带宽资源极其昂贵。如果让大文件直接经过业务服务器中转,会瞬间吃满服务器的网络上行/下行带宽与 CPU,导致其他正常的 API 业务请求超时卡死。使用预签名直传,客户端直接将文件流上传给专业的海量带宽对象存储(OSS/S3)节点,彻底释放了业务服务器的压力。
在 OSS 分片上传(Multipart Upload)中,
ETag扮演了什么角色?答:
ETag是 OSS 服务端在成功接收并保存某个文件分片(Part)后返回给客户端的唯一哈希校验码。客户端在上传完所有分片后,需要调用CompleteMultipartUpload接口,将所有分片的PartNumber及其对应的ETag列表提交给 OSS;OSS 校验所有分片 ETag 无误后,才会将这些分片合并为一个完整的目标文件。
速记
- 直传原则:业务服务器发预签名 URL,客户端直接 PUT 上传 OSS。
- 本地预压缩:图片用 Luban 算法压分辨率,视频降码率,节省 90% 流量。
- 大文件分片:20MB+ 视频走 MultipartUpload,记录 ETag 实现断点续传。