spring boot 3 中 gridfstemplate 拿不到,因其在 spring data mongodb 4.0+ 中被弃用且自动配置已移除,必须改用 gridfsbucket 手动创建并流式下载,同时需正确设置 content-disposition 头以支持中文和空格。

Spring Boot 3 下载 MongoDB GridFS 文件,核心问题不是“能不能下”,而是GridFsTemplate在 Spring Boot 3 + Spring Data MongoDB 4.x 中已被标记为 @Deprecated,且默认不再自动配置——直接沿用旧教程代码会返回 null 或抛 NoSuchBeanDefinitionException。
为什么 Spring Boot 3 的 GridFsTemplate 拿不到?
Spring Data MongoDB 4.0+(对应 Spring Boot 3.x)移除了对 GridFsTemplate 的自动装配支持。它转而推荐基于 GridFSBucket 的原生驱动操作,因为:
-
GridFsTemplate内部仍依赖已过时的com.mongodb.client.gridfs.GridFSBucket,但 Spring Boot 3 默认使用org.springframework.data.mongodb.core.gridfs.GridFsOperations接口抽象,而该接口不再绑定具体实现类 - 自动配置类
MongoDataAutoConfiguration不再声明GridFsTemplateBean,除非你显式启用@EnableMongoAuditing或手动定义 - 即使你强行 new 出
GridFsTemplate,它也无法访问到正确的MongoDatabase实例(因MongoTemplate默认不暴露底层MongoDatabase)
必须用 GridFSBucket 手动流式下载
Spring Boot 3 的标准做法是:注入 MongoDatabaseFactory,用它获取 MongoDatabase,再创建 GridFSBucket。关键点是「不能跳过数据库实例」、「不能用 GridFsTemplate.getResource()」。
示例 Controller 片段:
@RestController
public class FileDownloadController {
private final MongoDatabaseFactory mongoDatabaseFactory;
public FileDownloadController(MongoDatabaseFactory mongoDatabaseFactory) {
this.mongoDatabaseFactory = mongoDatabaseFactory;
}
@GetMapping("/download/{id}")
public void downloadFile(@PathVariable String id, HttpServletResponse response) throws IOException {
ObjectId fileId = new ObjectId(id);
MongoDatabase db = mongoDatabaseFactory.getMongoDatabase();
GridFSBucket bucket = GridFSBuckets.create(db); // 注意:默认 bucket 名是 "fs"
GridFSFindIterable files = bucket.find(Filters.eq("_id", fileId));
GridFSFile file = files.first();
if (file == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 强制下载头(中文 filename 必须用 filename*)
String filename = file.getFilename();
String encodedName = URLEncoder.encode(filename, StandardCharsets.UTF_8)
.replace("+", "%20");
response.setContentType(file.getContentType() != null ? file.getContentType() : "application/octet-stream");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + filename + "\"; filename*=UTF-8''" + encodedName);
try (GridFSDownloadStream stream = bucket.openDownloadStream(fileId);
ServletOutputStream out = response.getOutputStream()) {
byte[] buffer = new byte[8192];
int len;
while ((len = stream.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.flush();
}
}
}
注意:
-
GridFSBucket必须每次从MongoDatabase创建,不能单例复用(它本身无状态,但线程安全) - 不要用
bucket.find().first()后再调bucket.openDownloadStream()做两次查询——这是冗余的,应确保fileId准确,直接开流 -
response.getOutputStream()和stream都必须显式 close 或 try-with-resources,否则连接可能卡住
filename 中文乱码和空格截断怎么修?
浏览器对 Content-Disposition 的 filename= 参数解析极脆弱:空格变 +、中文直接丢、括号被截断。只靠 URLEncoder.encode(...) 不够。
正确写法分两步:
- 保留兼容字段:
filename="xxx"(供老浏览器 fallback) - 强制指定标准字段:
filename*=UTF-8''xxx(RFC 5987,Chrome/Firefox/Safari 均支持) - 编码时用
StandardCharsets.UTF_8,且对结果做.replace("+", "%20")——因为URLEncoder把空格编成+,但 RFC 5987 要求空格是%20
错误示例:filename="测试.pdf" → 下载名变成 "?.pdf";filename="my file.pdf" → 变成 "my 截断。
真正容易被忽略的是:Spring Boot 3 下你不能再依赖“配置好就能用”的黑盒模板,GridFSBucket 是唯一稳定路径,且每个下载响应都必须亲手控制流、设头、编码 filename —— 少一步,前端就卡在预览页或下出乱码文件。











