GridFsTemplate不支持分片上传,仅负责将完整InputStream写入GridFS;GB级文件直接调用store()会因内存溢出、HTTP体限制或缺乏断点续传而失败,需前端分片+后端合并再调用。

GridFsTemplate 本身不解决大文件上传的“分片”或“流式写入”问题,它只负责把已准备好的 InputStream 写入 GridFS;真正决定能否传大文件的,是上传链路的前端切片、后端落盘策略、HTTP 层配置和 MongoDB 的 chunk 处理机制 —— 而不是 GridFsTemplate.store() 这一行代码。
为什么直接用 GridFsTemplate.store() 传 GB 级文件会失败
这不是 GridFsTemplate 的缺陷,而是误用了它的定位:
- 它默认把整个
InputStream读进内存再分块写入{bucket}.chunks,若前端一次性 POST 一个 2GB 文件,Spring Boot 的MultipartFile会先尝试加载到 JVM 堆中,极易触发OutOfMemoryError - Tomcat/Nginx 默认限制单请求体大小(如 Nginx 的
client_max_body_size、Tomcat 的max-http-post-size),不调大会直接返回 413 错误 -
GridFsTemplate.store()是原子操作:要么全写入,要么全失败,无法感知“已传 80%”,更不支持断点续传 - MongoDB GridFS 本身按 255KB 分块存储,但这个分块发生在
store()执行时,不是上传前的协商过程
必须配合分片上传流程使用 GridFsTemplate
大文件上传的本质是「前端分片 + 后端暂存 + 合并写入」,GridFsTemplate 只在最后一步起作用:
- 前端用
File.slice()切出 5MB/片,带参数fileId、chunkIndex、totalChunks发起多个小请求 - 后端收到每个分片后,不走
MultipartFile,改用@RequestBody byte[]或InputStream直接接收二进制流,立刻落盘到临时目录(如/tmp/upload/{fileId}/{index}) - 当所有分片收齐,用
RandomAccessFile或Files.concat()合并为完整文件流,再交给GridFsTemplate.store(InputStream, filename, contentType, metadata) - 合并完成即删临时文件,避免磁盘占满
GridFsTemplate 构造时漏传 bucket 名是最大隐患
绝大多数线上故障不是因为文件太大,而是因为 bucket 隔离失效导致查不到或删错:
- 不传
bucket字符串时,GridFsTemplate固定操作fs.files和fs.chunks,哪怕你配了spring.data.mongodb.grid-fs-database=custom_db也无效 - 要隔离「临时上传中」和「已发布」文件,必须声明两个 bean:
@Bean(name = "gridTempFsTemplate")构造传"tempFs",@Bean(name = "gridFormalFsTemplate")构造传"formalFs" - 注入时必须用
@Autowired @Qualifier("gridTempFsTemplate"),否则 Spring 会因类型冲突报错 - 合并后的正式文件应写入
formalFs,临时分片文件绝不进正式桶 —— 这个边界一旦模糊,后续清理逻辑就会误删生产数据
contentType 和 metadata 的硬性约束不能绕过
这两个参数看着可选,但填错会导致后续查询完全失效:
-
contentType是第三个必填参数,传null或空字符串会导致 MongoDB 写入contentType: null,之后调用gridFsTemplate.findByContentType("image/png")查不到 -
metadata必须用Document.parse("{\"type\":\"report\",\"userId\":\"u_123\"}")构建,字段名含.或$(如{"user.id": "123"})会抛IllegalArgumentException - 文件名(第二个参数)只存进
filename字段,MongoDB 不校验唯一性,同名文件靠_id区分 —— 所以不要依赖文件名做查询主键
真正麻烦的从来不是怎么把字节流塞进 MongoDB,而是分片状态如何持久化、失败后如何清理临时文件、bucket 名是否贯穿前后端一致 —— 这些细节没对齐,上传接口上线三天就可能开始丢文件。











