使用GridFS前需主动初始化fs.files和fs.chunks集合,避免“Collection not found”错误;上传须用AsyncRead+Unpin类型(如tokio::fs::File或Cursor),下载应流式处理而非全量加载,chunk大小设为4MB并从新业务起统一规范。

用 gridfs_bucket() 创建桶引用前,先确认数据库连接已就绪
驱动不会在调用 gridfs_bucket() 时立即创建 fs.files 和 fs.chunks 集合,也不会建索引——只有第一次写入(如 upload_from_stream())触发后,集合和索引才真正落地。如果后续读取操作早于首次写入,会遇到 Collection fs.files not found 类错误。
实操建议:
- 在应用启动阶段主动执行一次轻量写入(例如上传一个空字节流并立刻删除),确保 bucket 基础结构就位
- 或显式检查集合是否存在:
my_db.collection("fs.files").count_documents(doc! {}, None).await,为 0 则说明未初始化 - 不要依赖
GridFsBucketOptions::builder().bucket_name("custom")就认为自定义桶已就绪——它只影响后续命名,不触发创建
upload_from_stream() 必须配 tokio::fs::File 或内存 Cursor,不能传裸指针
Rust 驱动的上传接口要求实现 AsyncRead + Unpin,常见误用是把 &[u8] 或 *const u8 直接塞进去,编译会报错 trait bound not satisfied;更隐蔽的问题是用 std::fs::File(同步句柄),导致阻塞 runtime。
实操建议:
- 大文件走
tokio::fs::File::open(path).await?,再传给upload_from_stream() - 小文件或测试数据用
std::io::Cursor::new(Vec::from(&b"hello"[..])),注意Vec<u8></u8>要 owned,不能借&[u8] - 若需带元数据,构造
GridFsUploadOptions:设置chunk_size_bytes(推荐4 * 1024 * 1024)、metadata字段(Bson::Document 类型)
open_download_stream() 返回流,别用 read_to_end() 全加载
这个函数返回的是 GridFsDownloadStream,本质是封装了对 fs.chunks 的游标迭代。若直接调 read_to_end(),等于把整个文件所有 chunk 拉到内存再拼——100MB 文件就占 100MB 内存,且 chunk 间无预读,I/O 是纯顺序、高延迟的。
实操建议:
- 下游是文件写入:用
stream.copy_to(&mut file).await?,底层走零拷贝tokio::io::copy - 下游是网络响应:将
GridFsDownloadStream包装成tokio_util::io::StreamReader,再 pipe 给hyper::Response::builder().body(Body::wrap(stream)) - 需要随机偏移读取(如视频 seek):不用此 API,改用
bucket.find()查出目标 range 对应的n列表,再逐个find_one()拿data字段手动拼接
chunk 大小设为 4MB 后,已有文件不会自动重分块
files.chunkSize 是只读字段,上传时指定的 chunk_size_bytes 仅影响新文件。线上已有大量 255KB chunk 的文件,不会因修改默认值而重建——它们仍按旧规则切分,查询时仍要处理 40+ 次磁盘寻道(机械盘)或多次网络 round-trip(分片集群)。
实操建议:
- 新业务必须从第一天就设
chunk_size_bytes: 4 * 1024 * 1024 - 存量迁移需重传:查出
fs.files中待处理文件 ID →bucket.open_download_stream()读出 → 新 bucket 以 4MB chunk 重传 → 成功后删旧文件 - 不要试图 patch
fs.files.chunkSize字段:驱动校验时会拒绝解析,且fs.chunks文档里的n编号与实际 data 长度已不匹配
真正卡住性能的往往不是驱动 API 本身,而是 chunk 边界和 I/O 模式没对齐——比如前端按 1MB 分片请求,而后端 chunk 是 255KB,结果每次都要读 4 个 chunk 再裁剪,OS page cache 彻底失效。这个细节在本地跑 demo 时完全看不出来,一上生产机械盘就暴露。


















