文件上传记录表必须包含 id、original_name、stored_name、path、size、mime_type、created_at 七个核心字段;其中 stored_name 为服务端生成的唯一文件名,path 为相对路径,mime_type 必须服务端校验,size 用 bigint 防超 2gb,id 建议 bigint unsigned auto_increment。

文件上传记录表必须包含哪些核心字段
直接回答:至少要有 id、original_name、stored_name、path、size、mime_type、created_at。缺任意一个,后续查问题或清理文件都会卡住。
其中 stored_name 是服务端生成的唯一文件名(如 20240521_abc789.jpg),不是原始名;path 存的是相对路径(如 uploads/avatar/),和 stored_name 拼起来才是完整路径;mime_type 必须存(不能只靠后缀判断),否则前端预览或安全校验会失效。
-
id建议用BIGINT UNSIGNED AUTO_INCREMENT,避免未来 ID 溢出 -
original_name要设varCHAR(255),中文文件名 UTF8MB4 下可能占 4 字节/字符 -
path不要存绝对路径(如/var/www/html/uploads/),否则迁移环境时全挂 -
size用BIGINT,单个文件可能超 2GB(比如视频)
要不要加关联字段?外键还是字符串引用
如果文件属于某个业务实体(比如用户头像、订单附件),必须加关联字段,但**不建议用外键约束**。
常见做法是加 owner_type + owner_id 组合(即“多态关联”),例如:
owner_type enum('user', 'order', 'article') NOT NULL, owner_id BIGINT UNSIGNED NOT NULL,
而不是为每个业务建单独字段(如 user_id、order_id)——那样改表太频繁,且空字段浪费空间。
- 外键会拖慢大批量插入(如导入 10 万张图片),还可能因级联删除误删文件记录
- 如果业务要求强一致性(如删用户必须删其所有头像),逻辑层做比数据库外键更可控
-
owner_type用ENUM比VARCHAR省空间,且能防非法值,但新增类型要改表结构
mysql 8.0+ 可以考虑的优化点
如果你用的是 MySQL 8.0 或更新版本,两个实用技巧能省事不少:
- 用
generated column自动提取扩展名:extension VARCHAR(16) AS (SUBSTRING_INDEX(original_name, '.', -1)) STORED,之后按类型查(如
WHERE extension IN ('jpg','png'))更快 - 给
path和stored_name加联合唯一索引:UNIQUE KEY `uk_path_name` (`path`, `stored_name`),防止同目录下重复写入覆盖
- 如果查询常带时间范围(如“查昨天上传的所有 PDF”),在
created_at上建前缀索引没用,直接建普通索引就行
容易被忽略但线上必踩的坑
最常出问题的不是字段设计,而是权限与生命周期管理没同步落地:
- 表里存了
path,但 Web 服务器(nginx/apache)没配对应静态路由,导致 URL 返回 404 - 没存
deleted_at字段,删文件只能物理删除,结果审计日志、回收站功能全没法做 -
mime_type全靠客户端传(如 js 的file.type),实际被绕过率 100%,必须服务端用file命令或库(如 Python 的python-magic)重新检测 - 批量上传时用事务包住多条 INSERT,但忘了把文件写磁盘的操作也包进去——数据库写了,文件没落盘,数据就对不上
真正麻烦的永远不是建表语句,而是文件路径怎么和部署路径对齐、删记录时要不要同步删磁盘文件、以及 CDN 缓存怎么刷新。这些得在表结构定稿前就和运维、前端对清楚。