Spring Data + MongoDB GridFS可以通过Repository来访问吗?

17

我最近发现了GridFS,想用它来存储带元数据的文件。我想知道是否可以使用 MongoRepository 来查询GridFS?如果可以,有人能给我一个例子吗?

如果有的话,我也可以使用Hibernate。

原因是:我的元数据包含很多不同的字段,使用存储库查询会比为每种情况编写一些new Query(Criteria.where(...))更容易。并且我还可以通过REST API仅提供Java对象而不是文件本身。

编辑:

  • Spring 4 Beta
  • Spring Data Mongo 1.3.1
  • Hibernate 4.3 Beta

1
你找到这个问题的解决方案了吗?我也遇到了同样的问题。 - Sami
嗨。我在下面添加了一个答案,其中包含我的当前“解决方案”。 - Benjamin M
现在有一个名为GridFsTemplate的工具。 - WildDev
2个回答

16

有一种方法可以解决这个问题:

@Document(collection="fs.files")
public class MyGridFsFile {

    @Id
    private ObjectId id;
    public ObjectId getId() { return id; }

    private String filename;
    public String getFilename() { return filename; }

    private long length;
    public long getLength() { return length; }

    ...

}

您可以编写一个普通的Spring Mongo Repo。现在,您至少可以使用Spring Data Repo查询fs.files集合。但是:您无法通过这种方式访问文件内容。

要获取文件内容本身,您至少有两个选择:

  1. 使用file = gridOperations.findOne(Query.query(Criteria.where("_id").is(id))); InputStream is = file.getInputStream();

  2. 查看GridFSDBFile的源代码。在那里,您可以看到它如何在内部查询fs.chunks集合并填充InputStream。

(选项2非常底层,选项1要容易得多,而且此代码由MongoDB-Java-Driver开发人员维护,尽管我会选择选项1)。


更新GridFS条目:

  • GridFS不适用于更新文件内容!
  • 尽管仅更新metadata字段可能有用。其余字段基本上是静态的。

您应该能够简单地使用自定义的MyGridFsFileRepoupdate方法。我建议metadata字段创建一个setter。


不同文件的不同元数据:

我使用具有通用元数据的abstract MyGridFsFile类来解决这个问题,例如:

@Document(collection="fs.files")
public abstract class AbstractMyGridFsFile<M extends AbstractMetadata> {

    ...

    private M metadata;
    public M getMetadata() { return metadata; }
    void setMetadata(M metadata) { this.metadata = metadata; }

}

当然,每个实现都有其自己的AbstractMetadata实现关联。我做了什么? AbstractMetadata始终具有名为type的字段。这样,我就可以找到正确的AbstractMyGridFsFile实现。尽管我还有一个通用的抽象存储库。

顺便说一下:同时,我从使用Spring Repo转换为使用纯粹通过MongoTemplate进行访问:

protected List<A> findAll(Collection<ObjectId> ids) {
    List<A> files = mongoTemplate.find(Query.query(Criteria
            .where("_id").in(ids)
            .and("metadata.type").is(type) // this is hardcoded for each repo impl
    ), typeClass); // this is the corresponding impl of AbstractMyGridFsFile
    return files;
}

希望这可以帮到你。如果你需要更多关于这方面的信息,我可以写更多,告诉我即可。


2
我的代码库有一个自定义方法,它使用 gridFsOperations.save(...) 来保存新文件。InputStream 本身不是 MyGridFsFile 的一部分,我通过 myRepo.getInputStreamForFile(MyGridFsFile file) 检索它。然后该方法调用 gridFsOperations.findOne(/* via file.getId() */).getInputStream()。... 当然,你可以将 InputStream 检索机制注入到你的 MyGridFsFile 中,但这样你就需要在这个 POJO 中添加一些代码逻辑,这并不好,但它可以工作。 - Benjamin M
1
我仍然不明白的是 file.getId() 如何匹配 fs.files 中文件的 Id?你是否将 MyGridFSFile 对象存储在普通的Mongo文档中,而将文件存储在GridFS中?如果是这样,它们是如何关联的? - Sami
2
PART 1: 好的,让我们从头开始!GridFS基本上只是两个MongoDB集合:fs.filesfs.chunksfs.files存储像idfilenamemd5等内容,而fs.chunks则存储文件内容。因此,当您使用GridFS存储文件时,它将简单地在fs.files中创建一个条目,并在fs.chunks中创建一些条目(取决于文件大小)。GridFS不是单独的数据存储,它只是这两个标准Mongo集合。虽然使用gridFsTemplate保存文件并随后对fs.files进行正常查询没有问题。 - Benjamin M
2
例子:使用 gridFsOperations.store(inputStream, filename, contentType, metadata); 存储图像。然后像这样查询 fs.filesmongoTemplate.find(new Query(), MyGridFsFile.class)。它将返回存储在 GridFS 中的所有文件列表(它查看 MyGridFsFile@Document 注释以找到正确的集合进行查询)。现在,您可以在返回的 MyGridFsFile 上调用 getId()。然后,您可以执行 GridFSDBFile file = gridFsOperations.findOne(Query.query(Criteria.where("_id").is(id))) 并调用 file.getInputStream() 来检索实际的文件内容。 - Benjamin M
1
第三部分:Mongo Java驱动程序:查看com.mongodb.gridfs.GridFS类的源代码。在那里,您可以看到文件如何持久化。它使用_bucketName + ".files"_bucketName + ".chunks",其中_bucketName默认为fs。如果您查看com.mongodb.gridfs.GridFSDBFile源代码,您可以看到它如何将文件拆分为块并保存它们(writeTo方法)。以及如何按正确顺序流式传输块以生成InputStreamgetInputStream方法)。这是一个相当低级别的操作 ;) - Benjamin M
显示剩余2条评论

3
你可以使用MongoTemplate创建一个GridFS对象,然后与之交互:
MongoTemplate mongoTemplate = new MongoTemplate(new Mongo(), "GetTheTemplateFromSomewhere");
GridFS gridFS = new GridFS(mongoTemplate.getDb());

GridFS对象让您可以创建、删除和查找等操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接