打开网易新闻 查看精彩图片

50MB文件变成70MB文本,塞进API再吐给存储。前端团队觉得"一个请求搞定"很优雅,直到用户用3G网络传视频时开始骂娘。

这不是什么底层架构难题。是个裹着糖衣的架构错误,我们吃了三年才发现。

「优雅」的代价:每传1MB要多烧40%流量

「优雅」的代价:每传1MB要多烧40%流量

Base64编码的设计初衷很单纯:把二进制塞进只认文本的系统。邮件系统年代的老古董,每个字节拆成6位,再映射到64个可见字符。

数学很残酷:每3字节二进制 → 4字节文本,固定33%膨胀率。

加上HTTP的文本传输开销,实际膨胀冲到40%左右。传50MB文件,用户流量账单上记的是70MB。弱网环境下,这多出来的20MB直接决定上传成功还是超时崩溃。

更隐蔽的损耗在服务端。API层先收完整文本,解码回二进制,再转给S3。内存里同时躺着编码版和解码版,峰值占用翻倍。Node.js服务为此频繁GC,上传高峰时CPU曲线像心电图。

我们监控里看到过诡异现象:上传进度条走到90%卡住,30秒后报错。不是S3的问题,是API层解码时内存爆了,进程重启。

分片上传的真实成本:不是复杂,是「看起来复杂」

分片上传的真实成本:不是复杂,是「看起来复杂」

转向S3 Multipart(分片上传)时,团队抵触情绪很高。前端嫌流程麻烦:要拆文件、算etag、管并发、处理失败重传。后端嫌状态难维护:上传ID生命周期、分片顺序、合并时机。

这些抱怨有个共同前提:把S3当黑盒,所有逻辑自己实现。

AWS SDK从2019年起就封装了高阶API。`Upload`类自动处理分片大小(默认5MB)、并发数(默认4线程)、失败重试。代码量对比:Base64方案需要手写解码和流控,分片上传反而是更短的调用链。

关键认知翻转:分片不是「把简单问题搞复杂」,是把隐藏成本摊到明面上。

Base64的复杂度被编码过程掩盖了。开发者看不见内存峰值,测不出流量损耗,直到生产环境炸锅。分片上传把网络抖动、重传策略、进度反馈全部暴露成可调参数,反而可控。

我们迁移后的数据:平均上传耗时从4.2分钟降到1.8分钟,失败率从12%压到0.3%。最意外的收益是带宽账单—— egress费用直接砍掉68%,因为流量不再经过API层中转。

为什么这个错误能活三年

为什么这个错误能活三年

复盘时有个尴尬发现:Base64方案是2019年一个实习生写的PR。代码干净、测试通过、评审没意见。当时文件平均大小2MB,WiFi环境为主,33%膨胀感知不明显。

产品迭代五年,文件大小中位数涨到47MB,移动端占比从30%冲到71%。架构债务像信用卡账单,最低还款额越滚越高。

更深层的原因是「抽象泄漏」的误判。团队把Base64当成「传输层细节」,认为可以无损替换。实际上它已经渗入业务逻辑:前端直接拿Base64字符串做预览图,后端用正则校验数据格式,甚至数据库有个字段存编码后前1KB用于「快速识别」。

迁移花了六周,其中四周在解耦这些隐形依赖。真正的技术债不是那行编码代码,是五年来所有人默认「这就是上传的样子」。

现在回头看那个「优雅」的设计

现在回头看那个「优雅」的设计

当时评审文档里有句话:「统一用JSON传数据,降低前后端沟通成本。」这句话本身没错,错在把「统一」当成目标而非手段。

文件上传不是数据,是流。流需要背压、需要续传、需要对网络质量做自适应。把这些硬塞进JSON的方框,就像用快递盒运自来水——能运,但每走一步都在漏。

有个细节值得玩味:迁移后我们保留了Base64入口,作为「兼容模式」开关。三个月过去,调用量归零。没有用户投诉,没有业务方挽留。那个曾经觉得「不可或缺」的便利,原来只是没人愿意第一个说皇帝没穿衣服。

最近整理技术债清单时,又看到2019年那个PR的备注:「TODO:后续考虑直传S3」。批注日期是2019年11月。

你的代码库里,有多少个「TODO」已经活成了化石?