迁移后 files.md5 失效是常态,因其仅由标准 API 计算写入,直连插入或 mongorestore 不重算,且 MongoDB 4.4+ 已弃用该字段;应改用 metadata.file_sha256 存储 SHA-256 哈希并建索引,秒传逻辑基于该字段查询,同时需校验文件长度以防 chunk 丢失。

GridFS 集群迁移后 files.md5 字段批量不一致,基本不是校验逻辑出错,而是迁移过程绕过了 GridFS 标准写入路径——该字段只在驱动调用 upload_from_stream() 等标准 API 时由客户端计算并写入,直连 mongod 插入 fs.files 文档或 mongodump/mongorestore 迁移时不会重算,导致字段残留旧值、空值或 Base64 编码乱码。
为什么迁移后 files.md5 失效是常态,不是例外
原集群若用 PyMongo upload_from_stream() 写入,files.md5 是十六进制小写字符串;但用 mongorestore 恢复 fs.files 集合时,该字段原样复制,不校验也不更新。更常见的是:旧系统用 Node.js 驱动传了 options.md5(Base64 编码),而新集群 Python 脚本按 hex 解析,比对必然失败。另外,MongoDB 4.4+ 已明确弃用 files.md5 字段,官方文档不再保证其行为一致性。
不要修复 files.md5,改用自定义哈希字段做迁移后校验
迁移完成后,对每个文件执行一次「本地重算 + 写入元数据」的原子操作,把真实哈希存到 metadata.file_sha256(比 MD5 更可靠):
- 用流式方式读取 GridFS 文件内容(避免 OOM),调用
hashlib.sha256()分块更新,最后digest('hex') - 用
fs.find_one({"_id": file_id})查出原文件,再调用fs.collection.files.update_one({"_id": file_id}, {"$set": {"metadata.file_sha256": "xxx"}}) - 务必提前在
fs.files上为metadata.file_sha256建索引:db.fs.files.createIndex({"metadata.file_sha256": 1}) - 跳过
files.md5字段——它已不可信,也不再被任何现代驱动默认填充
同步脚本里如何安全跳过已存在文件(秒传逻辑)
迁移后的秒传不能查 files.md5,必须查你刚写入的 metadata.file_sha256:
- 上传前本地算好
file_sha256,然后fs.find({"metadata.file_sha256": "xxx"}).limit(1).next() - 不要用
count_documents(),它无法利用索引快速终止,大数据量下极慢 - 命中即返回
_id和filename,前端可立即响应“秒传成功”,无需触发下载 - 如果迁移前老系统根本没存任何哈希,首次运行脚本时需全量重算并补全
metadata.file_sha256,后续增量只需处理新增文件
最容易被忽略的一点:即使 metadata.file_sha256 匹配,也得在目标集群上实际调用 fs.open_download_stream() 并读取全部字节,校验 length 是否与 files.length 一致——因为 chunk 丢失或 WiredTiger 页面损坏,可能让元数据和哈希都“看起来正常”,但文件内容已残缺。

















