如何使用 Spring 数据 MongoDB 通过 GridFS ObjectId 获取二进制流
How to get a binary stream by GridFS ObjectId with Spring Data MongoDB
当我已经拥有正确的 ObjectId
.
GridFSTemplate returns GridFSResource
(getResource()
) 或 GridFSFile
(findX()
)。
我可以通过 ID:
得到 GridFSFile
// no way to get the InputStream?
GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(id)))
但是没有明显的方法如何为 GridFSFile
.
获得 InputStream
只有GridFSResource
能让我得到对应的InputStream
和InputStreamResource#getInputstream
。但是获得 GridFSResource
的唯一方法是通过它的 filename
.
// no way to get GridFSResource by ID?
GridFSResource resource = gridFsTemplate.getResource("test.jpeg");
return resource.getInputStream();
不知何故 GridFsTemplate
API 暗示文件名是唯一的——但事实并非如此。 GridFsTemplate
实现只是 returns 第一个元素。
现在我正在使用本机 MongoDB API 一切又变得有意义了:
GridFS gridFs = new GridFs(mongo);
GridFSDBFile nativeFile = gridFs.find(blobId);
return nativeFile.getInputStream();
看来我误解了 Spring 数据 Mongo GridFS 抽象背后的基本概念。我希望(至少)以下内容之一是 possible/true:
- 通过 ID
获取 GridFSResource
- 得到一个
GridFSResource
或 InputStream
一个 GridFsFile
我已经有
我是不是错了,或者 Spring 数据 MongoDB API 的这个特定部分有什么奇怪的?
GridFSDBFile file = ...
ByteArrayOutputStream baos = new ByteArrayOutputStream();
file.writeTo(baos);
byte[] ba = baos.toByteArray()
这些类型有点乱:
- GridFSFile 是来自 MongoDB 驱动程序的类型
- GridFsResource 是 Spring
的类型
- ObjectId 是来自 BSON API
的类型
来自 Spring GridFsTemplate source:
public getResource(String location) {
GridFSFile file = findOne(query(whereFilename().is(location)));
return file != null ? new GridFsResource(file, getGridFs().openDownloadStream(location)) : null;
}
有一个丑陋的解决方案:
@Autowired
private GridFsTemplate template;
@Autowired
private GridFsOperations operations;
public InputStream loadResource(ObjectId id) throws IOException {
GridFSFile file = template.findOne(query(where("_id").is(id)));
GridFsResource resource = template.getResource(file.getFilename());
GridFSFile file = operations.findOne(query(where("_id").is(id)));
GridFsResource resource = operations.getResource(file.getFilename());
return resource.getInputStream();
}
我也偶然发现了这个。我真的很震惊 GridFsTemplate 的设计是这样的......
无论如何,到目前为止,我的丑陋 "solution":
public GridFsResource download(String fileId) {
GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
return new GridFsResource(file, getGridFs().openDownloadStream(file.getObjectId()));
}
private GridFSBucket getGridFs() {
MongoDatabase db = mongoDbFactory.getDb();
return GridFSBuckets.create(db);
}
注意:您必须注入 MongoDbFactory 才能工作...
您是否考虑过将 Spring Content 用于 Mongo 作为您解决方案的内容存储部分?
假设您正在使用 Spring 引导以及 Spring 数据 Mongo 那么它可能类似于以下内容:
pom.xml
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-mongo-boot-starter</artifactId>
<version>0.0.10</version>
</dependency>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest-boot-starter</artifactId>
<version>0.0.10</version>
</dependency>
更新您的 Spring 数据 Mongo 实体,具有以下属性:
@ContentId
private String contentId;
@ContentLength
private long contentLength = 0L;
@MimeType
private String mimeType;
添加商店界面:
@StoreRestResource(path="content")
public interface MongoContentStore extends ContentStore<YourEntity, String> {
}
这就是您所需要的。当您的应用程序启动时 Spring Content 将看到对 Spring Content Mongo/REST 模块的依赖,它会为 GridFs 注入 MongonContenStore
存储的实现以及支持完整 CRUD 功能并将这些操作映射到底层存储接口的控制器。 REST 端点将在 /content
.
下可用
即
curl -X PUT /content/{entityId}
将创建或更新实体的图像
curl -X GET /content/{entityId}
将获取实体的图像
curl -X DELETE /content/{entityId}
将删除实体的图片
有几个入门指南here. They use Spring Content for the filesystem but the modules are interchangeable. The Mongo reference guide is here. And there is a tutorial video here。
HTH
我找到了解决这个问题的方法!
只需将 GridFSFile 包装在 GridFsResource 中!这旨在使用 GridFSFile 进行实例化。
public GridFsResource getUploadedFileResource(String id) {
var file = this.gridFsTemplate.findOne(new Query(Criteria.where("_id").is(id)));
return new GridFsResource(file);
}
@GetMapping("/{userId}/files/{id}")
public ResponseEntity<InputStreamResource> getUploadedFile(
@PathVariable Long userId,
@PathVariable String id
){
var user = userService
.getCurrentUser()
.orElseThrow(EntityNotFoundException::new);
var resource = userService.getUploadedFileResource(id);
try {
return ResponseEntity
.ok()
.contentType(MediaType.parseMediaType(resource.getContentType()))
.contentLength(resource.contentLength())
.body(resource);
} catch (IOException e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
这样做的最大好处是,您可以直接将 GridFsResource 传递给 ResponseEntity,因为 GridFsResource 扩展了 InputStreamResource。
希望对您有所帮助!
您好
尼克拉斯
将 GridFSFile 包装在 GridFsResource 中或使用此
GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
GridFsResource resource = gridFsTemplate.getResource(file);
return resource.getInputStream();
GridFsTemplate 的 getResource(com.mongodb.client.gridfs.model.GridFSFile file) 函数 returns GridFSFile 的 GridFsResource。
GridFSFile gridfsFile= gridFsTemplate.findOne(new
Query(Criteria.where("filename").is(fileName)));
GridFsResource gridFSResource= gridFsTemplate.getResource(gridfsFile);
InputStream inputStream= gridFSResource.getInputStream();
如果上面的方法在某些更高版本的 Spring 引导中不起作用,请使用以下命令:
GridFSFile gridfsFile= gridFsTemplate.findOne(new
Query(Criteria.where("filename").is(fileName)));
//or
GridFSFile gridfsFile =
gridFsOperations.findOne(Query.query(Criteria.where("filename").is(fileName)));
return ResponseEntity.ok()
.contentLength(gridFsdbFile.getLength())
.contentType(MediaType.valueOf("image/png"))
.body(gridFsOperations.getResource(gridFsdbFile));
@RequestMapping(value = "/api ")
public class AttachmentController {
private final GridFsOperations gridFsOperations;
@Autowired
public AttachmentController(GridFsOperations gridFsOperations) {
this.gridFsOperations = gridFsOperations;
}
@GetMapping("/file/{fileId}")
public ResponseEntity<Resource> getFile(@PathVariable String fileId) {
GridFSFile file =
gridFsOperations.findOne(Query.query(Criteria.where("_id").is(fileId)));
return ResponseEntity.ok()
.contentLength(file.getLength())
.body(gridFsOperations.getResource(file));
}
我知道是个老问题,但在 2019 年尝试使用 WebFlux 来做这个,我必须执行以下操作
public Mono<GridFsResource> getImageFromDatabase(final String id) {
return Mono.fromCallable(
() ->
this.gridFsTemplate.getResource(
Objects.requireNonNull(
this.gridFsTemplate.findOne(new Query(Criteria.where("_id").is(id))))
.getFilename()));
}
这会给你一个 Mono
,它可以在控制器中返回。不过,我确定有更好的解决方案。
Spring Data 2.1.0 添加了 getResource()
到 GridFsTemplate
的重载,对于给定的 GridFsFile
returns GridFsResource
。 GridFsResource
有一个获取 InputStream
的方法。因此,如果您至少使用此版本的 Spring 数据,则可以通过两次调用 GridFsTemplate
:
来获取 InputStream
GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(id)));
// In real code, make sure you perform any necessary null checks if the file doesn't exist
GridFsResource resource = gridFsTemplate.getResource(gridFsFile);
InputStream inputStream = resource.getInputStream();
当我已经拥有正确的 ObjectId
.
GridFSTemplate returns GridFSResource
(getResource()
) 或 GridFSFile
(findX()
)。
我可以通过 ID:
得到GridFSFile
// no way to get the InputStream?
GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(id)))
但是没有明显的方法如何为 GridFSFile
.
InputStream
只有GridFSResource
能让我得到对应的InputStream
和InputStreamResource#getInputstream
。但是获得 GridFSResource
的唯一方法是通过它的 filename
.
// no way to get GridFSResource by ID?
GridFSResource resource = gridFsTemplate.getResource("test.jpeg");
return resource.getInputStream();
不知何故 GridFsTemplate
API 暗示文件名是唯一的——但事实并非如此。 GridFsTemplate
实现只是 returns 第一个元素。
现在我正在使用本机 MongoDB API 一切又变得有意义了:
GridFS gridFs = new GridFs(mongo);
GridFSDBFile nativeFile = gridFs.find(blobId);
return nativeFile.getInputStream();
看来我误解了 Spring 数据 Mongo GridFS 抽象背后的基本概念。我希望(至少)以下内容之一是 possible/true:
- 通过 ID 获取
- 得到一个
GridFSResource
或InputStream
一个GridFsFile
我已经有
GridFSResource
我是不是错了,或者 Spring 数据 MongoDB API 的这个特定部分有什么奇怪的?
GridFSDBFile file = ...
ByteArrayOutputStream baos = new ByteArrayOutputStream();
file.writeTo(baos);
byte[] ba = baos.toByteArray()
这些类型有点乱:
- GridFSFile 是来自 MongoDB 驱动程序的类型
- GridFsResource 是 Spring 的类型
- ObjectId 是来自 BSON API 的类型
来自 Spring GridFsTemplate source:
public getResource(String location) {
GridFSFile file = findOne(query(whereFilename().is(location)));
return file != null ? new GridFsResource(file, getGridFs().openDownloadStream(location)) : null;
}
有一个丑陋的解决方案:
@Autowired
private GridFsTemplate template;
@Autowired
private GridFsOperations operations;
public InputStream loadResource(ObjectId id) throws IOException {
GridFSFile file = template.findOne(query(where("_id").is(id)));
GridFsResource resource = template.getResource(file.getFilename());
GridFSFile file = operations.findOne(query(where("_id").is(id)));
GridFsResource resource = operations.getResource(file.getFilename());
return resource.getInputStream();
}
我也偶然发现了这个。我真的很震惊 GridFsTemplate 的设计是这样的...... 无论如何,到目前为止,我的丑陋 "solution":
public GridFsResource download(String fileId) {
GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
return new GridFsResource(file, getGridFs().openDownloadStream(file.getObjectId()));
}
private GridFSBucket getGridFs() {
MongoDatabase db = mongoDbFactory.getDb();
return GridFSBuckets.create(db);
}
注意:您必须注入 MongoDbFactory 才能工作...
您是否考虑过将 Spring Content 用于 Mongo 作为您解决方案的内容存储部分?
假设您正在使用 Spring 引导以及 Spring 数据 Mongo 那么它可能类似于以下内容:
pom.xml
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-mongo-boot-starter</artifactId>
<version>0.0.10</version>
</dependency>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest-boot-starter</artifactId>
<version>0.0.10</version>
</dependency>
更新您的 Spring 数据 Mongo 实体,具有以下属性:
@ContentId
private String contentId;
@ContentLength
private long contentLength = 0L;
@MimeType
private String mimeType;
添加商店界面:
@StoreRestResource(path="content")
public interface MongoContentStore extends ContentStore<YourEntity, String> {
}
这就是您所需要的。当您的应用程序启动时 Spring Content 将看到对 Spring Content Mongo/REST 模块的依赖,它会为 GridFs 注入 MongonContenStore
存储的实现以及支持完整 CRUD 功能并将这些操作映射到底层存储接口的控制器。 REST 端点将在 /content
.
即
curl -X PUT /content/{entityId}
将创建或更新实体的图像
curl -X GET /content/{entityId}
将获取实体的图像
curl -X DELETE /content/{entityId}
将删除实体的图片
有几个入门指南here. They use Spring Content for the filesystem but the modules are interchangeable. The Mongo reference guide is here. And there is a tutorial video here。
HTH
我找到了解决这个问题的方法!
只需将 GridFSFile 包装在 GridFsResource 中!这旨在使用 GridFSFile 进行实例化。
public GridFsResource getUploadedFileResource(String id) {
var file = this.gridFsTemplate.findOne(new Query(Criteria.where("_id").is(id)));
return new GridFsResource(file);
}
@GetMapping("/{userId}/files/{id}")
public ResponseEntity<InputStreamResource> getUploadedFile(
@PathVariable Long userId,
@PathVariable String id
){
var user = userService
.getCurrentUser()
.orElseThrow(EntityNotFoundException::new);
var resource = userService.getUploadedFileResource(id);
try {
return ResponseEntity
.ok()
.contentType(MediaType.parseMediaType(resource.getContentType()))
.contentLength(resource.contentLength())
.body(resource);
} catch (IOException e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
这样做的最大好处是,您可以直接将 GridFsResource 传递给 ResponseEntity,因为 GridFsResource 扩展了 InputStreamResource。
希望对您有所帮助!
您好 尼克拉斯
将 GridFSFile 包装在 GridFsResource 中或使用此
GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
GridFsResource resource = gridFsTemplate.getResource(file);
return resource.getInputStream();
GridFsTemplate 的 getResource(com.mongodb.client.gridfs.model.GridFSFile file) 函数 returns GridFSFile 的 GridFsResource。
GridFSFile gridfsFile= gridFsTemplate.findOne(new
Query(Criteria.where("filename").is(fileName)));
GridFsResource gridFSResource= gridFsTemplate.getResource(gridfsFile);
InputStream inputStream= gridFSResource.getInputStream();
如果上面的方法在某些更高版本的 Spring 引导中不起作用,请使用以下命令:
GridFSFile gridfsFile= gridFsTemplate.findOne(new
Query(Criteria.where("filename").is(fileName)));
//or
GridFSFile gridfsFile =
gridFsOperations.findOne(Query.query(Criteria.where("filename").is(fileName)));
return ResponseEntity.ok()
.contentLength(gridFsdbFile.getLength())
.contentType(MediaType.valueOf("image/png"))
.body(gridFsOperations.getResource(gridFsdbFile));
@RequestMapping(value = "/api ")
public class AttachmentController {
private final GridFsOperations gridFsOperations;
@Autowired
public AttachmentController(GridFsOperations gridFsOperations) {
this.gridFsOperations = gridFsOperations;
}
@GetMapping("/file/{fileId}")
public ResponseEntity<Resource> getFile(@PathVariable String fileId) {
GridFSFile file =
gridFsOperations.findOne(Query.query(Criteria.where("_id").is(fileId)));
return ResponseEntity.ok()
.contentLength(file.getLength())
.body(gridFsOperations.getResource(file));
}
我知道是个老问题,但在 2019 年尝试使用 WebFlux 来做这个,我必须执行以下操作
public Mono<GridFsResource> getImageFromDatabase(final String id) {
return Mono.fromCallable(
() ->
this.gridFsTemplate.getResource(
Objects.requireNonNull(
this.gridFsTemplate.findOne(new Query(Criteria.where("_id").is(id))))
.getFilename()));
}
这会给你一个 Mono
,它可以在控制器中返回。不过,我确定有更好的解决方案。
Spring Data 2.1.0 添加了 getResource()
到 GridFsTemplate
的重载,对于给定的 GridFsFile
returns GridFsResource
。 GridFsResource
有一个获取 InputStream
的方法。因此,如果您至少使用此版本的 Spring 数据,则可以通过两次调用 GridFsTemplate
:
InputStream
GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(id)));
// In real code, make sure you perform any necessary null checks if the file doesn't exist
GridFsResource resource = gridFsTemplate.getResource(gridFsFile);
InputStream inputStream = resource.getInputStream();