应使用 GridFSBucket而非旧版GridFS,因其支持多bucket隔离、灵活查询;需手动设chunk_size_bytes和metadata(含content_type),并预先创建索引。

直接用 GridFSBucket 批量存图没问题,但必须手动构造 metadata 并控制 chunk_size_bytes,否则默认 255KB 分块会导致小图浪费空间、大图上传慢且内存压力高。
为什么不用 GridFS 而选 GridFSBucket
旧版 GridFS 类是全局命名空间,多个 bucket 会冲突;GridFSBucket 支持显式指定 bucket_name,适合多业务隔离(比如 "images" 和 "thumbnails" 分开)。它还支持按 _id 或自定义字段查文件,比老接口灵活得多。
- 必须传
database实例,不能只传连接对象 - 不自动创建索引,首次使用前建议手动建:
db.fs.files.create_index("filename") - 如果已有同名文件,
upload_from_stream()默认覆盖——没 warning,也不报错
上传前必须处理的三个关键参数
光调 upload_from_stream() 不行,图片元信息和分块策略得自己定:
-
chunk_size_bytes:默认 255KB。对 PNG/JPEG 小图(64 * 1024(64KB)更省空间;对 >10MB 的 TIFF,可提到1024 * 1024(1MB)减少 chunk 数量 -
metadata字典里至少加"content_type"(如"image/jpeg"),否则前端无法正确解析 MIME - 文件名别硬编码,用
os.path.basename(path)提取,避免路径注入或斜杠混乱
示例:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
立即学习“Python免费学习笔记(深入)”;
from gridfs import GridFSBucket
import os
<p>bucket = GridFSBucket(db, bucket_name="images", chunk_size_bytes=64*1024)
with open("/path/to/photo.jpg", "rb") as f:
file_id = bucket.upload_from_stream(
filename=os.path.basename("/path/to/photo.jpg"),
source=f,
metadata={
"content_type": "image/jpeg",
"uploaded_at": datetime.utcnow(),
"original_size": os.stat("/path/to/photo.jpg").st_size
}
)批量导入时最容易崩的两个坑
循环上传看似简单,但并发、异常、重复名三者叠加极易出错:
- 不要在循环里反复 new
GridFSBucket实例——它内部缓存了 collection 引用,频繁初始化会拖慢速度 - 遇到
FileExists错误(其实是gridfs.errors.FileExists)要捕获,不是OSError;默认行为是覆盖,但你可能想跳过或重命名 - 大目录下建议加
time.sleep(0.01)防止瞬间打满 MongoDB 连接池(尤其副本集未配置读写分离时)
实际跑通的关键在于:先测单张,再控速批量,最后补索引。GridFSBucket 本身不难,难的是把文件系统路径、HTTP MIME、MongoDB 存储粒度这三层对齐——漏掉任意一层,查的时候就只能靠 _id 硬翻。

















