mongofiles 已从 MongoDB 6.0+ 中移除,应改用 pymongo 的 GridFS;需显式指定 filename 和 metadata,避免内存溢出、重复上传及孤儿块问题,并校验上传结果。

用 mongofiles 上传单个文件时提示 “command not found”
这是最常见的起步障碍:mongofiles 已从 MongoDB 6.0+ 版本中正式移除,不再随 Server 或 Shell 一起分发。如果你装的是较新版本的 MongoDB(比如通过 brew install mongodb-community 或官方 .deb/.rpm 包),默认就没有这个工具。
解决办法只有两个:降级到 5.0.x(不推荐),或改用驱动程序脚本。实际项目中,用 Python + pymongo 是最可控、可批量、可容错的选择。
- 确认当前版本:
mongod --version或mongo --version - 如果输出是
6.0.0及以上,别再找mongofiles二进制了 -
pymongo支持 GridFS 原生操作,且能精确控制filename、metadata、chunk_size等关键字段
Python 脚本批量写入 GridFS 的最小可靠写法
核心不是“怎么存”,而是“怎么避免重复存、怎么记录 ID、怎么处理大文件”。直接用 GridFS.put() 很容易踩坑——比如没设 filename,后续就只能靠 _id 查,没法按名删;又或者没传 metadata,后期无法按业务维度筛选。
from pymongo import MongoClient
from gridfs import GridFS
import os
<p>client = MongoClient("mongodb://localhost:27017/")
db = client["myapp"]
fs = GridFS(db)</p><p>for filepath in ["./data/report.pdf", "./data/config.json"]:
with open(filepath, "rb") as f:</p><h1>必须显式指定 filename,否则 GridFS 不会自动提取</h1><pre class="brush:php;toolbar:false;"> file_id = fs.put(
f,
filename=os.path.basename(filepath),
metadata={"source": "batch_upload", "uploaded_at": "2024-05-20"}
)
print(f"Uploaded {filepath} → {file_id}")
-
filename是 GridFS 查询的关键字段,漏掉会导致后续fs.find({"filename": ...})查不到 - 不要依赖
fs.put(f.read())把整个文件读进内存——大文件(>100MB)会 OOM;保持open(..., "rb")+ 文件对象传入 -
fs.put()返回的是ObjectId,不是字符串,别误当成路径用
上传后查不到文件?检查 fs.files 和 fs.chunks 是否分离
GridFS 实际由两个集合组成:fs.files 存元数据(含 _id、filename、length 等),fs.chunks 存分块数据。如果只看到 fs.chunks 有数据但 fs.files 为空,说明 put() 中途失败(比如网络断开、磁盘满),或用了错误的数据库实例。
- 手动验证:
db.fs.files.find({filename: "report.pdf"}).pretty() - 如果返回空,但
db.fs.chunks.countDocuments({files_id: ObjectId("xxx")})有结果,说明写入不完整,该文件已损坏,必须重传 - 确保
GridFS(db)中的db和你 shell 里use myapp是同一个数据库名,大小写敏感
需要并发上传?别直接开多线程跑 fs.put()
GridFS.put() 内部会先插入 fs.files 文档,再分块写 fs.chunks。如果多个线程同时写同名 filename,MongoDB 不会报错,但后写的会覆盖前写的 fs.files 记录,导致旧 chunks 成为“孤儿块”(orphaned chunks),浪费空间且难以清理。
- 若需并发,必须保证每个文件的
filename全局唯一(例如加时间戳或随机后缀) - 或者用单线程 +
asyncio+motor驱动,避免 GIL 阻塞,同时保持逻辑串行 - 生产环境强烈建议加上传前校验:
if fs.exists(filename=...): continue,避免重复
实际批量上传最麻烦的从来不是“怎么塞进去”,而是“怎么确认每一份都干净落库、可查、可删、不残留”。哪怕只是临时脚本,也值得在 fs.put() 后补一句 assert fs.exists(filename=...)。











