Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 or InputStream for a GridFsFile I already have

Am I wrong or is there something odd with this particular piece of the Spring Data MongoDB API?

like image 881
atamanroman Avatar asked Mar 07 '18 14:03

atamanroman


People also ask

How does MongoDB GridFS work?

Instead of storing a file in a single document, GridFS divides the file into parts, or chunks [1], and stores each chunk as a separate document. By default, GridFS uses a default chunk size of 255 kB; that is, GridFS divides a file into chunks of 255 kB with the exception of the last chunk.

Which collections are used to store GridFS data in MongoDB?

GridFS divides a file into chunks and stores each chunk of data in a separate document, each of maximum size 255k. GridFS by default uses two collections fs. files and fs.

What is GridFS bucket?

A GridFS “bucket” is the combination of an “fs. files” and “fs. chunks” collection which together represent a bucket where GridFS files can be stored.


2 Answers

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...

like image 99
Steffen Jacobs Avatar answered Oct 18 '22 17:10

Steffen Jacobs


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();
}
like image 30
deii Avatar answered Oct 18 '22 18:10

deii