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.
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
There is a bit mess in these types:
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();
}
                                                                        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();
                                                                        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));
                                                                        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...
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.