GridFS图片马赛克需先read()转bytes再用OpenCV处理,视频转码须流式分块读取+ffmpeg pipe;打码/转码后需存新file_id并同步业务表metadata,避免垃圾文件堆积。

GridFS 文件读取时无法直接用 OpenCV 处理
GridFS 存储的是二进制分块数据,gridfs.GridFS.find_one() 返回的是类似文件对象的 GridOut 实例,不是 bytes 或 numpy array。直接传给 cv2.imdecode() 会报错:error: (-215:Assertion failed) buf != nullptr。
- 必须先调用
.read()获取完整 bytes,再用np.frombuffer(..., dtype=np.uint8)转为数组 - 对大图(如 >5MB)要警惕内存峰值——
.read()会一次性加载全部 chunk,别在高并发场景无缓冲地调用 - 如果只打局部马赛克(比如人脸区域),可考虑用
io.BytesIO(gridout.read())+PIL.Image.open(),比 OpenCV 内存友好些
Python 中对 GridFS 图片做实时马赛克(非存档重写)
核心是「读 → 解码 → 处理 → 编码 → 写回」四步闭环,但不能覆盖原文件,得生成新 file_id。常见错误是漏掉 content_type 或 filename,导致前端无法识别 MIME 类型。
- 用
cv2.resize()+cv2.resize()反向缩放实现马赛克:先缩小再放大,比均值滤波更可控 - 打码后务必重设
content_type,例如原图是image/jpeg,处理后仍需显式传入content_type="image/jpeg" - 别用
gridfs.put()直接传 numpy array,它只接受 bytes、str 或 file-like object;正确做法是cv2.imencode(".jpg", img_mosaic)[1].tobytes()
ret, buf = cv2.imencode(".jpg", img_mosaic)
new_id = fs.put(buf.tobytes(), filename="masked_" + orig_name, content_type="image/jpeg")
GridFS 视频转码必须绕过内存瓶颈
视频文件通常远超单次 .read() 容量,强行加载会 OOM。根本解法是流式处理:用 gridout 的 read() 分段读取 + ffmpeg-python 的 pipe 输入,避免落地临时文件。
- 关键参数:设置
gridout.seek(0)确保从头读;用gridout.read(1024*1024)分 1MB 块喂给 ffmpeg stdin - ffmpeg 命令里必须加
-f mp4 -i -表示从 stdin 读 raw 流,否则会卡在 probe 阶段 - 转码失败常见报错:
Invalid data found when processing input,基本是分块读取时破坏了 MP4 atom 结构——此时只能退回到先下载到磁盘再转,无法纯流式
打码/转码后的文件如何关联原始记录
GridFS 本身不维护元数据关系,靠应用层绑定。容易被忽略的是:更新业务表时没同步清理旧打码文件,造成 GridFS 里垃圾文件堆积。
- 推荐在业务文档里加字段
masked_file_id和transcoded_file_id,类型为ObjectId,指向 GridFS 中对应_id - 删除原始图片时,别只删 GridFS 主文件,要用
fs.delete(orig_id)+fs.delete(masked_id)成对清理 - 如果用
fs.find({"metadata.source_id": orig_id})查衍生文件,记得提前在metadata里存好索引字段,不然全表扫描极慢










