上传功能写起来半小时,出问题能拖垮整个系统。磁盘写满、文件被越权下载、同名覆盖,几乎每个项目都踩过。
一、先定存储方式
| 方式 | 适合 | 代价 |
|---|---|---|
| 服务器本地磁盘 | 单机、小体量、内网系统 | 扩容麻烦,多实例无法共享 |
| 共享存储(NAS) | 多台应用服务器共享文件 | 需处理挂载与权限,性能看网络 |
| 对象存储 | 图片、附件、视频,量大 | 要处理签名 URL、跨域、流量费用 |
| 数据库存二进制 | 极小文件、强事务要求 | 备份膨胀、查询变慢,能不用就不用 |
选型的判断标准是"文件会不会增长到单机放不下"以及"应用会不会多实例部署"。两条任意一条成立,就选对象存储。
二、上传链路:大文件要分片
- 小文件(<10MB):直接表单上传,前端限制类型与大小,后端再校验一次
- 大文件:分片上传,每片 5–10MB,支持断点续传;先初始化拿到 uploadId,全部完成后合并
- 秒传:按内容哈希查重,命中直接返回已有地址,但要注意权限隔离
- 超时与重试:单请求设置超时,失败只重传失败分片,不要整包重来
反向代理和网关的上传大小限制经常被忘记,表现为"本地上传正常,线上 413",先检查 client_max_body_size 这类配置。
三、安全检查:五道关
- 类型:不只看扩展名,校验 MIME 与文件头魔数;白名单比黑名单可靠
- 大小:服务端强制上限,前端限制只是体验
- 文件名:不使用用户原始文件名落盘,改为 UUID 或哈希 + 原扩展名
- 路径:拒绝
../与绝对路径,防止目录穿越 - 内容:可执行文件、脚本、带宏文档按策略拦截或转存隔离区,必要时接杀毒扫描
上传目录绝不能可执行
把上传目录放在 Web 根目录下且允许脚本执行,是最典型的高危配置。文件与执行路径必须分离,最好放在独立域名或对象存储。
四、访问控制:私有是默认值
- 对象存储桶默认私有,读取通过短期签名 URL(有效期几分钟到几小时)
- 下载接口必须校验"当前用户对该业务对象有无权限",不能只凭文件 ID
- 敏感附件(合同、身份证、体检报告)加下载水印与日志
- 禁止目录列举;错误提示不要泄露文件是否存在
常见漏洞是文件 ID 可枚举:改成 UUID,并在服务端做业务归属校验,两者缺一不可。
五、生命周期:不清就是定时炸弹
存储成本不会一次爆掉,而是慢慢涨。设计时就要定规则:
- 临时文件:未完成的分片 24 小时清理;上传成功但未提交表单的,7 天清理
- 业务文件:随业务对象删除而删除,或标记软删除后延迟清理
- 配额:按用户或租户设总量上限,超限提示而不是静默失败
- 监控:磁盘使用率、对象存储增长量、单日上传量,三项都做告警
再加一条兜底:备份策略要明确排除还是包含附件。附件不备份,恢复时业务数据全在、文件全丢,这种事故并不少见。
#文件上传#对象存储#访问控制#开发实践