代码之家  ›  专栏  ›  技术社区  ›  atamanroman

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

  •  17
  • atamanroman  · 技术社区  · 7 年前

    我不知道如何使用spring数据mongodb及其 GridFSTemplate 当我已经有权利 ObjectId

    GridFSTemplate返回 GridFSResource ( getResource() )或 GridFSFile ( findX() )。

    我可以得到 GridFSFile 按ID:

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

    但是没有明显的方法 InputStream 为此 GridFSFile

    只有 网格资源 让我能掌握相关信息 输入流 具有 InputStreamResource#getInputstream 。但唯一的办法是 网格资源 按其 filename

    // no way to get GridFSResource by ID?
    GridFSResource resource = gridFsTemplate.getResource("test.jpeg");
    return resource.getInputStream();
    

    不知怎么的 GridFsTemplate API意味着文件名是唯一的,而不是唯一的。这个 Gridfs模板 实现只返回第一个元素。

    现在,我正在使用本机MongoDB API,一切都再次变得有意义:

    GridFS gridFs = new GridFs(mongo);
    GridFSDBFile nativeFile = gridFs.find(blobId);
    return nativeFile.getInputStream();
    

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

    • 获取 网格资源 按其ID
    • 获取 网格资源 输入流 对于 GridFsFile 我已经有了

    我错了吗?或者这段特殊的Spring数据MongoDB API有什么奇怪的地方吗?

    10 回复  |  直到 7 年前
        1
  •  11
  •   Steffen Jacobs    7 年前

    我也偶然发现了这一点。实际上,我很震惊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才能工作。。。

        2
  •  6
  •   deii    7 年前

    这些类型有点混乱:

    来自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();
    }
    
        3
  •  1
  •   Niklas    7 年前

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

    只需将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。

    希望这有帮助!

    您好 尼克拉斯

        4
  •  1
  •   Paul Warren    5 年前

    您是否考虑过使用 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模块的依赖关系,它将注入 MongonContenStore store for GridFs以及支持完整CRUD功能并将这些操作映射到底层store接口的控制器实现。REST端点将在下可用 /content

    i、 e。

    curl -X PUT /content/{entityId} 将创建或更新实体的图像

    curl -X GET /content/{entityId} 将获取实体的图像

    curl -X DELETE /content/{entityId} 将删除实体的图像

    有几个入门指南 here 。它们在文件系统中使用Spring内容,但模块可以互换。Mongo参考指南是 here 。还有一段教程视频 here

    HTH公司

        5
  •  0
  •   Bodil    6 年前

    将GridFSFile包装到GridFsResource中,或使用

    GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
    GridFsResource resource = gridFsTemplate.getResource(file);
    return resource.getInputStream();
    
        6
  •  0
  •   Rohit Basu    6 年前

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

    如果上述方法在某些更高版本的弹簧护套中不起作用,请使用波纹管:

    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));
    
        7
  •  0
  •   Rohit Basu    6 年前
    @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));
    }
    
        8
  •  0
  •   Chris    6 年前

    我知道一个老问题,但为了在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 可以在控制器中返回。不过,我相信有一个更好的解决方案。

        9
  •  0
  •   M. Justin Marwan Alsabbagh    5 年前

    弹簧数据2.1.0增加了过载 getResource() GridFsTemplate 返回 GridFsResource 对于给定的 GridFsFile 网格资源 具有获取 InputStream 。因此,如果您至少使用此版本的Spring数据,那么您可以获得 输入流 打两个电话给 Gridfs模板 :

    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();
    
        10
  •  -3
  •   Steve H    7 年前
    GridFSDBFile file = ... 
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    file.writeTo(baos);
    byte[] ba = baos.toByteArray()