如何使用Spring Data MongoDB通过GridFS ObjectId获取二进制流

时间:2018-03-07 14:08:45

标签: mongodb spring-data spring-data-mongodb

当我已经拥有正确的GridFSTemplate时,我无法弄清楚如何使用spring-data-mongodb及其ObjectId从GridFS流式传输二进制文件。

GridFSTemplate返回GridFSResourcegetResource())或GridFSFilefindX())。

我可以通过ID获取GridFSFile

// no way to get the InputStream?
GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(id)))

但是如何为InputStream获取GridFSFile没有明显的方法。

只有GridFSResource允许我抓住InputStreamInputStreamResource#getInputstream相对应的GridFSResource。但获得filename的唯一方法是// no way to get GridFSResource by ID? GridFSResource resource = gridFsTemplate.getResource("test.jpeg"); return resource.getInputStream();

GridFsTemplate

不知何故,GridFsTemplate API意味着文件名是唯一的 - 它们不是。 GridFS gridFs = new GridFs(mongo); GridFSDBFile nativeFile = gridFs.find(blobId); return nativeFile.getInputStream(); 实现只返回第一个元素。

现在我使用本机MongoDB API,一切都有意义:

GridFSResource

看起来我误解了Spring Data Mongo GridFS抽象背后的基本概念。我希望(至少)以下事情之一是可能的/真实的:

  • 按ID <{li>获取GridFSResource
  • 为我已经拥有的InputStream获取GridFsFilegroup_by(id)

我错了,或者Spring Data MongoDB API的这个特定部分有什么奇怪的东西吗?

10 个答案:

答案 0 :(得分:10)

我也偶然发现了这一点。我真的很震惊,GridFsTemplate的设计就像这样...... 无论如何,到目前为止,我的丑陋“解决方案”:

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才能工作......

答案 1 :(得分:5)

这些类型有点乱:

来自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();
}

答案 2 :(得分:1)

我找到了解决这个问题的方法!

只需将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扩展了InputStreamResource,因此您可以直接将GridFsResource传递给ResponseEntity。

希望这会有所帮助!

问候 尼古拉斯

答案 3 :(得分:0)

您是否考虑过将Spring Content用于Mongo作为解决方案中的内容存储部分?

假设您正在使用Spring Boot以及Spring Data 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 Data 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。他们将Spring Content用于文件系统,但模块可以互换。 Mongo参考指南是here。还有一个教程视频here

HTH

答案 4 :(得分:0)

将GridFSFile包装在GridFsResource中或使用

!something           -> 0
!                    -> 0
something!           -> 9
"something!"         -> -1
"some!thing"!        -> 12
!"!"!                -> 0
""!                  -> 2
""!""                -> 2

答案 5 :(得分:0)

GridFsTemplate的

getResource(com.mongodb.client.gridfs.model.GridFSFile文件)函数返回GridFSFile的GridFsResource。

GridFSFile gridfsFile= gridFsTemplate.findOne(new 
Query(Criteria.where("filename").is(fileName)));
GridFsResource gridFSResource= gridFsTemplate.getResource(gridfsFile);
InputStream inputStream= gridFSResource.getInputStream();

如果以上版本在某些更高版本的Spring Boot中不起作用,请使用以下波纹管:

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));

答案 6 :(得分:0)

@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));
}

答案 7 :(得分:0)

我知道一个老问题,但是尝试在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,可以在控制器中将其返回。我敢肯定有更好的解决方案。

答案 8 :(得分:0)

Spring Data 2.1.0向GridFsTemplate添加了getResource()的重载,该重载返回给定GridFsResource的{​​{1}}。 GridFsFile有一种获取GridFsResource的方法。因此,如果您至少使用此版本的Spring Data,则可以通过两次调用InputStream来获得InputStream

GridFsTemplate

答案 9 :(得分:-3)

<section id="events-list">
  <div class="event">
    <div class="event-date">
      <p>MAR 30</p>
    </div>
    <div class="event-details">
      <p class="event-city">City, US</p>
      <p class="event-bus">Company HQ - Company Double Line</p>
      <a class="register" href="#">RSVP</a>
    </div>
  </div>
  <div class="event">
    <div class="event-date">
      <p>MAR 30</p>
    </div>
    <div class="event-details">
      <p class="event-city">City, US</p>
      <p class="event-bus">Company HQ</p>
      <a class="register" href="#">RSVP on 4/13</a>
    </div>
  </div>
  <div class="event">
    <div class="event-date">
      <p>MAR 30</p>
    </div>
    <div class="event-details">
      <p class="event-city">City, US</p>
      <p class="event-bus">Company HQ - Company Double Line</p>
      <a class="register" href="#">RSVP</a>
    </div>
  </div>
</section>