mongodb go驱动需显式引入x/exp/gridfs包,上传用openuploadstream+io.copy避免oom,下载用opendownloadstream+io.copy流式响应,删除仅清files文档需手动清理孤儿chunks,查询须用bucket.find而非直查集合。

用 mongo-go-driver 的 gridfs 包上传文件
Go 官方驱动不内置 GridFS,得显式引入 go.mongodb.org/mongo-driver/x/exp/gridfs(注意是 x/exp/,非稳定版)。上传前必须先有有效的 *mongo.Database 实例和可写的 bucket。
- 别漏掉
import "go.mongodb.org/mongo-driver/x/exp/gridfs",否则编译报undefined: gridfs - bucket 名默认是
fs,但实际集合名是fs.files和fs.chunks;改名需传gridfs.WithBucketName("mybucket") - 上传大文件时,
UploadFromStream接收io.Reader,别直接传[]byte——容易 OOM;用bytes.NewReader或文件句柄更安全 - 示例:
b, _ := gridfs.NewBucket(db) uploadStream, _ := b.OpenUploadStream("report.pdf") io.Copy(uploadStream, fileReader) uploadStream.Close()
从 GridFS 读取文件内容并写入响应流
常见于 Web 服务返回图片或 PDF,核心是避免把整个文件加载进内存。用 OpenDownloadStream 拿到 gridfs.File,它实现了 io.ReadCloser。
- 调用
File.Size()前务必检查err == nil,否则可能 panic(比如文件不存在时Size()返回 0 且无错误,但后续读取会失败) - HTTP 响应头要设
Content-Length和Content-Type,否则浏览器可能无法正确解析;Content-Type得从File.ContentType取,不是猜 - 别用
io.ReadAll读取整个文件再写——100MB 文件就崩了;用io.Copy(w, file)直接流式转发 - 示例:
file, err := b.OpenDownloadStream(id) if err != nil { /* 404 */ } defer file.Close() w.Header().Set("Content-Length", strconv.FormatInt(file.Size(), 10)) w.Header().Set("Content-Type", file.ContentType()) io.Copy(w, file)
删除文件时为什么 Delete 不报错但文件还在
gridfs.Bucket.Delete 只删 files 集合文档,不自动清理关联的 chunks 数据——这是设计行为,不是 bug。MongoDB 官方驱动不会做级联删除。
采用HttpClient向服务器端action请求数据,当然调用服务器端方法获取数据并不止这一种。WebService也可以为我们提供所需数据,那么什么是webService呢?,它是一种基于SAOP协议的远程调用标准,通过webservice可以将不同操作系统平台,不同语言,不同技术整合到一起。 实现Android与服务器端数据交互,我们在PC机器java客户端中,需要一些库,比如XFire,Axis2,CXF等等来支持访问WebService,但是这些库并不适合我们资源有限的android手机客户端,
- 确认是否真删了:查
fs.files集合,该文档应已消失;再查fs.chunks,对应files_id的 chunk 还在 - 手动清理
chunks很危险:必须用和Delete同一事务或同一时间点的files_id,否则可能误删其他文件的块 - 稳妥做法是定期跑后台任务,用
fs.chunks的files_id去反查fs.files是否还存在,只删“孤儿 chunk” - 如果业务要求强一致性,建议换用普通集合存文件元数据 + 对象存储(如 S3),GridFS 的维护成本其实高于预期
查询 GridFS 文件元信息的坑:不能直接用 Find 查 fs.files
fs.files 是普通集合,但它的 _id 字段类型可能是 ObjectID、string 或自定义类型,取决于上传时传的 options.FileID。直接 collection.Find 容易因类型不匹配查不到。
立即学习“go语言免费学习笔记(深入)”;
- 想按文件名查,用
b.Find(gridfs.Bucket提供的方法),它内部处理了字段映射和类型转换 - 想按自定义字段(如
user_id)查,得先确保上传时写了该字段:b.UploadFromStream("a.pdf", r, options.GridFSUpload().SetMetadata(bson.M{"user_id": 123})) -
Find返回的是*gridfs.File切片,不是原始 BSON 文档;要拿原始数据,得用file.FileID、file.UploadDate等导出字段,别试图file.Decode - 注意
Find不支持 sort / limit 的链式调用,得用options.GridFSFind().SetSort(...)
GridFS 在 Go 里不是开箱即用的黑盒,每个操作背后都绑着 MongoDB 底层集合行为和驱动版本约束。最常被忽略的是 x/exp/ 包的稳定性风险——它可能在下个大版本消失,而且不保证 API 兼容。









