文件上传系统

很多 SaaS 产品都会遇到文件上传:头像、图片、PDF、合同、附件、数据表、音频、视频、导入文件。

文件上传看起来只是一个表单功能,但真正上线后会牵扯很多问题:文件大小、格式校验、存储路径、权限控制、安全扫描、上传进度、失败重试、CDN、删除策略和成本。

如果一开始设计太随意,后面文件一多,系统会很难维护。

不要把文件直接存进数据库

新手最容易犯的错误,是把文件内容直接存进数据库。

数据库适合存结构化数据,比如用户、订单、项目、文件记录;不适合存大量二进制文件。文件本身应该放到对象存储里,比如 S3、R2、OSS、COS、GCS 等。

数据库里只保存文件元数据:

arduino 复制代码
file_id
owner_id
bucket
object_key
file_name
mime_type
size
status
created_at

这样数据库负责"文件是谁的、在哪里、是什么状态",对象存储负责保存真正的文件内容。

上传路径不要直接暴露业务结构

文件在对象存储里的路径要设计清楚。

不要使用用户原始文件名作为唯一路径,也不要把敏感业务信息直接写进路径。原始文件名可能重复,可能包含特殊字符,也可能暴露隐私。

更推荐使用系统生成的 key:

复制代码
uploads/{user_id}/{file_id}/{safe_file_name}

或者:

复制代码
uploads/{yyyy}/{mm}/{file_id}

文件展示时可以保留原始文件名,但存储路径应该由系统控制。

推荐使用预签名上传

早期很多人会让文件先上传到自己的业务服务器,再由服务器转存到对象存储。

这种方式简单,但大文件一多,业务服务器会承受大量带宽和内存压力。更好的方式是预签名上传。

流程是:

markdown 复制代码
1. 前端请求后端创建上传凭证
2. 后端校验用户权限和文件信息
3. 后端生成预签名 URL
4. 前端直接上传到对象存储
5. 上传完成后通知后端确认
6. 后端更新文件状态

这样文件数据不需要经过你的业务服务器,系统更稳,也更省资源。

上传前要做基本校验

不要相信前端传来的文件。

上传前后都要做校验:文件大小、扩展名、MIME 类型、业务限制、用户剩余额度、上传次数、文件数量。

前端校验用于减少用户等待,后端校验用于保护系统。对象存储上传策略也可以限制文件大小和类型。

例如:

bash 复制代码
头像:只允许 jpg/png/webp,最大 5MB
PDF:只允许 application/pdf,最大 20MB
导入表格:只允许 csv/xlsx,最大 10MB
视频:限制格式、大小和时长

校验规则越清楚,后面越少脏数据。

上传完成不等于文件可用

文件上传成功,只代表文件到了存储系统,不代表它已经可以使用。

很多文件还需要后续处理:图片压缩、缩略图生成、病毒扫描、PDF 解析、音视频转码、表格解析、内容安全检测。

因此文件表里应该有状态字段:

arduino 复制代码
pending_upload
uploaded
processing
ready
failed
deleted

用户上传完成后,可以先显示"处理中",等后台任务处理完再变成可用。

文件权限要单独设计

文件权限不能只靠 URL 隐藏。

如果是私有文件,用户访问时应该先经过你的业务系统校验权限,再生成临时访问 URL,或者通过受控下载接口返回。

常见权限问题包括:

css 复制代码
用户 A 访问用户 B 的文件
团队成员访问已删除项目文件
公开分享链接泄露私密文件
离职成员仍能访问旧文件
临时 URL 有效期太长

文件通常包含隐私和业务数据,权限设计要比普通页面更谨慎。

公共文件和私有文件要分开

不是所有文件都需要私有。

比如公开头像、公开封面图、文章配图,可以放在公共 bucket 或走 CDN。合同、发票、导入数据、用户附件、内部文档,应该放在私有 bucket。

公共文件追求访问速度和缓存,私有文件追求权限安全和可审计。

不要把所有文件都混在一个权限模型里。

上传进度和失败重试很重要

文件上传和普通表单不同,用户会等待。

如果上传大文件时没有进度条、没有取消按钮、没有失败提示,用户会不知道系统是否卡住。

至少要提供:

复制代码
上传进度
上传中状态
失败提示
重新上传
取消上传
大小限制提示
格式限制提示

文件上传体验越清楚,客服问题越少。

删除文件要有策略

用户删除文件时,是否立刻从对象存储删除?

这要看业务。很多系统会先软删除数据库记录,再异步清理对象存储。这样可以避免误删,也能给恢复和审计留空间。

你需要考虑:

复制代码
用户删除后是否可恢复
文件是否被多个业务对象引用
对象存储是否有生命周期策略
是否需要定期清理孤儿文件
是否需要保留审计记录

文件删除不是简单的 delete,尤其在团队协作和付费业务里。

成本要提前估算

文件系统的成本不只包括存储。

还包括上传流量、下载流量、CDN 流量、图片处理、视频转码、请求次数、备份、跨区域复制。

如果你的产品允许用户上传大文件或视频,成本可能很快上升。早期就要设置大小限制、配额和清理策略。

不要等账单异常后才想起限制上传。

一个文件上传系统结构

可以按下面结构设计:

复制代码
前端:
选择文件 / 校验 / 进度 / 重试

后端:
权限校验 / 创建文件记录 / 生成预签名 URL / 确认上传

对象存储:
保存原始文件 / 权限控制 / 生命周期

后台任务:
扫描 / 压缩 / 解析 / 转码 / 生成缩略图

数据库:
文件元数据 / 状态 / 所属用户 / 权限 / 业务关联

这比"一个上传接口"更接近真实系统。

写在最后

文件上传系统的关键,不是把文件传上去,而是让文件从上传、存储、处理、访问、删除到审计都有清晰规则。

早期最推荐的结构是:对象存储保存文件,数据库保存元数据,预签名 URL 直接上传,后台任务异步处理,权限访问单独控制。

下一篇,我们继续聊存储选择:图片存储怎么选

相关推荐
止语Lab3 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活3 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困3 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn3 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530143 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908403 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金3 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛4 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
IT_陈寒4 小时前
Vue的响应式让我加班到凌晨3点,原来问题出在这
前端·人工智能·后端