文件元数据必须单独建表,即创建files表存储id、original_name等字段,业务表仅存外键attachment_id;大文件禁用BLOB,应存路径并配合服务端校验文件名与mimetype,删除需事务化保障一致性。

文件元数据必须单独建表,不要把文件内容塞进业务表
直接在用户表或订单表里加一个 file_content BLOB 字段看似省事,但会导致主表膨胀、备份变慢、查询变卡,尤其当业务表本身已有高频读写时。MySQL 对大字段(如 BLOB/TEXT)的处理机制是:即使没查该字段,只要 SELECT * 或字段在索引覆盖之外,就可能触发额外的磁盘 I/O 去读取溢出页。
正确做法是拆成两张表:
-
files表存元数据:id、original_name、storage_path、size_bytes、mimetype、uploader_id、created_at - 业务表(如
orders)只存外键attachment_id或 JSON 数组存多个file_id(需配合应用层校验)
注意:storage_path 推荐存相对路径(如 2024/06/15/abc123.png),而非绝对 URL 或物理路径,方便后续迁移到对象存储(如 OSS/S3)时做透明替换。
大文件别用 BLOB 存 MySQL,用文件系统 + 路径引用
MySQL 官方明确建议:BLOB 不适合存储 > 1MB 的文件。实际压测中,单条记录超 4MB 就容易触发 max_allowed_packet 限制,且复制延迟、主从同步稳定性都会下降。
上传流程应为:
- 前端分片上传(可选)或服务端接收完整二进制流
- 生成唯一文件名(如 UUIDv4 + 时间戳哈希),写入本地磁盘或挂载的 NFS 目录(如
/data/uploads/...) - 仅将文件路径、哈希值(
md5或sha256)、大小等写入files表 - 定时任务清理无关联的物理文件(通过 LEFT JOIN 查
files中id不在任何业务表外键中的记录)
如果用云存储,storage_path 可存为 oss://bucket-name/path/to/file.jpg,应用层统一解析并生成预签名 URL,MySQL 本身不感知存储后端。
文件名和 mimetype 必须由服务端重写,不能信前端传的
前端提交的 filename 和 Content-Type 可被任意篡改,常见攻击包括:../../etc/passwd 路径遍历、shell.php 伪装图片、text/plain 冒充 image/jpeg 绕过校验。
部分功能简介:商品收藏夹功能热门商品最新商品分级价格功能自选风格打印结算页面内部短信箱商品评论增加上一商品,下一商品功能增强商家提示功能友情链接用户在线统计用户来访统计用户来访信息用户积分功能广告设置用户组分类邮件系统后台实现更新用户数据系统图片设置模板管理CSS风格管理申诉内容过滤功能用户注册过滤特征字符IP库管理及来访限制及管理压缩,恢复,备份数据库功能上传文件管理商品类别管理商品添加/修改/
服务端要做三件事:
- 忽略原始
filename,用服务端生成的 ID 重命名(如7f8a2b1e-9c4d-4e6f-ba7c-3d9e8a1f2b4c.jpg) - 用
file命令(Linux)或libmagic库真实检测二进制头,覆盖前端传的mimetype - 对允许类型做白名单检查(如只允
image/jpeg、application/pdf),拒绝一切application/x-php、text/html等可执行/渲染类型
MySQL 层可加 CHECK 约束(8.0.16+):CONSTRAINT chk_mimetype CHECK (mimetype IN ('image/jpeg','image/png','application/pdf')),但不能替代服务端校验。
删除文件要事务化:先删数据库记录,再删物理文件
顺序反了会留下“孤儿文件”——数据库里找不到记录,但磁盘上还占空间;更糟的是,如果删库成功、删文件失败,下次上传同名文件可能被误覆盖或报错。
推荐做法:
- 用数据库事务包裹
DELETE FROM files WHERE id = ? - 事务提交成功后,异步发消息(如 RabbitMQ/Kafka)或调用清理队列,由独立 worker 执行物理删除
- 若必须同步删,确保
unlink()或rm命令失败时抛异常并回滚整个事务(框架需支持嵌套事务或补偿逻辑)
另外,files 表建议加软删除字段 deleted_at DATETIME NULL,避免误删不可逆;物理清理任务只扫 deleted_at IS NOT NULL AND deleted_at 的记录。
最易被忽略的是:不同环境(开发/测试/生产)的 storage_path 根目录必须隔离,否则本地调试时一删可能清掉测试服的文件。









