How to get a binary stream by GridFS ObjectId with Spring Data MongoDB

前端 未结 10 1231
暗喜
暗喜 2021-02-02 15:00

I can\'t figure out how to stream a binary file from GridFS with spring-data-mongodb and its GridFSTemplate when I already have the right ObjectId.

相关标签:
10条回答
  • 2021-02-02 15:04

    i discovered the solution to this problem!

    Just wrap the GridFSFile in a GridFsResource! This is designed to be instantiated with a 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);
        }
    
    
    }
    

    The great advantage of this is, that you can directly pass the GridFsResource to a ResponseEntity due to the fact, that the GridFsResource extends a InputStreamResource.

    Hope this helps!

    Greetings Niklas

    0 讨论(0)
  • 2021-02-02 15:05

    There is a bit mess in these types:

    • GridFSFile is type from MongoDB driver
    • GridFsResource is type from Spring
    • ObjectId is type from BSON API

    From Spring GridFsTemplate source:

    public getResource(String location) {
    
        GridFSFile file = findOne(query(whereFilename().is(location)));
        return file != null ? new GridFsResource(file, getGridFs().openDownloadStream(location)) : null;
    }
    

    There is an ugly solution:

    @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();
    }
    
    0 讨论(0)
  • 2021-02-02 15:07

    Spring Data 2.1.0 added an overload of getResource() to GridFsTemplate that returns the GridFsResource for a given GridFsFile. GridFsResource has a method to get the InputStream. Therefore, if you're on at least this version of Spring Data, you can get the InputStream by making two calls to the GridFsTemplate:

    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();
    
    0 讨论(0)
  • 2021-02-02 15:15

    getResource(com.mongodb.client.gridfs.model.GridFSFile file) function of GridFsTemplate returns the GridFsResource for a GridFSFile.

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

    If the above one is not working in some higher version of Spring boot, use the bellow:

    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));
    
    0 讨论(0)
  • 2021-02-02 15:17

    I stumbled upon this, too. And I am actually pretty shocked that the GridFsTemplate has been designed like this... Anyway, my ugly "solution" to this so far:

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

    Note: You have to inject the MongoDbFactory for this to work...

    0 讨论(0)
  • 2021-02-02 15:21

    Old question I know, but trying to do this in 2019 using WebFlux, I had to do the following

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

    Which will give you a Mono which can be returned in a controller. I'm sure there is a nicer solution however.

    0 讨论(0)
提交回复
热议问题