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
.
GridFSTemplate returns either GridFSResource
(getResource()
) or GridFSFile
(findX()
).
I can get the GridFSFile
by ID:
// no way to get the InputStream?
GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(id)))
but there is no obvious way how to get an InputStream
for that GridFSFile
.
Only GridFSResource
allows me to get hold of the corresonding InputStream
with InputStreamResource#getInputstream
. But the only way to get a GridFSResource
is by its filename
.
// no way to get GridFSResource by ID?
GridFSResource resource = gridFsTemplate.getResource("test.jpeg");
return resource.getInputStream();
Somehow the GridFsTemplate
API implies that filenames are unique - which they are not. The GridFsTemplate
implementation just returns the first element.
Now I'm using the native MongoDB API and everything makes sense again:
GridFS gridFs = new GridFs(mongo);
GridFSDBFile nativeFile = gridFs.find(blobId);
return nativeFile.getInputStream();
It looks like I'm misunderstanding the fundamental concepts behind the Spring Data Mongo GridFS abstraction. I'd expect (at least) one of the following things to be possible/true:
- get a
GridFSResource
by its ID - get a
GridFSResource
orInputStream
for aGridFsFile
I already have
Am I wrong or is there something odd with this particular piece of the Spring Data MongoDB API?
Wrap the GridFSFile in a GridFsResource or use this
Have you condidered using Spring Content for Mongo for the content storage piece on your solution?
Assuming you are using Spring Boot as well as Spring Data Mongo then it might look something like the following:
Update your Spring Data Mongo entity, with the following attributes:
Add a store interface:
That's all that you need. When you application starts Spring Content will see the dependencies on the Spring Content Mongo/REST modules and it will inject an implementation of the
MongonContenStore
store for GridFs as well as an implementation of a controller that supports full CRUD functionality and maps those operations down onto the underlying store interface. The REST endpoint will be available under/content
.i.e.
curl -X PUT /content/{entityId}
will create or update an entity's imagecurl -X GET /content/{entityId}
will fetch the entity's imagecurl -X DELETE /content/{entityId}
will delete the entity's imageThere are a couple of getting started guides 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
i discovered the solution to this problem!
Just wrap the GridFSFile in a GridFsResource! This is designed to be instantiated with a GridFSFile.
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
Old question I know, but trying to do this in 2019 using WebFlux, I had to do the following
Which will give you a
Mono
which can be returned in a controller. I'm sure there is a nicer solution however.There is a bit mess in these types:
From Spring GridFsTemplate source:
There is an ugly solution:
getResource(com.mongodb.client.gridfs.model.GridFSFile file) function of GridFsTemplate returns the GridFsResource for a GridFSFile.
If the above one is not working in some higher version of Spring boot, use the bellow: