Spring Data MongoDB中的GridFS

2023/05/18

1. 概述

本教程将探讨Spring Data MongoDB的核心功能之一:与GridFS交互

GridFS存储规范主要用于处理超过BSON文档大小限制16MB的文件。Spring Data提供了一个GridFsOperations接口及其实现GridFsTemplate,以轻松地与这个文件系统交互。

2. 配置

2.1 XML配置

让我们从GridFsTemplate的简单XML配置开始:

<bean id="gridFsTemplate" class="org.springframework.data.mongodb.gridfs.GridFsTemplate">
    <constructor-arg ref="mongoDbFactory"/>
    <constructor-arg ref="mongoConverter"/>
</bean>

GridFsTemplate的构造函数参数包括对mongoDbFactory的bean引用(它创建一个Mongo数据库),以及mongoConverter(在Java和MongoDB类型之间进行转换)。它们的bean定义如下。

<mongo:db-factory id="mongoDbFactory" dbname="test" mongo-client-ref="mongoClient" />

<mongo:mapping-converter id="mongoConverter" base-package="cn.tuyucheng.taketoday.zoneddatetime.converter">
    <mongo:custom-converters base-package="cn.tuyucheng.taketoday.zoneddatetime.converter"/>
</mongo:mapping-converter>

2.2 Java配置

让我们创建一个类似的配置,只使用Java:

@Configuration
@EnableMongoRepositories(basePackages = "cn.tuyucheng.taketoday")
public class MongoConfig extends AbstractMongoClientConfiguration {
    @Autowired
    private MappingMongoConverter mongoConverter;

    @Bean
    public GridFsTemplate gridFsTemplate() throws Exception {
        return new GridFsTemplate(mongoDbFactory(), mongoConverter);
    }

    // ...
}

对于此配置,我们使用mongoDbFactory()方法并自动注入了在父类AbstractMongoClientConfiguration中定义的MappingMongoConverter。

3. GridFsTemplate核心方法

3.1 store

store方法将文件保存到MongoDB中。

假设我们有一个空数据库并希望在其中存储一个文件:

InputStream inputStream = new FileInputStream("src/main/resources/test.png"); 
gridFsTemplate.store(inputStream, "test.png", "image/png", metaData).toString();

请注意,我们可以通过将DBObject传递给store方法来将其他元数据与文件一起保存。对于我们的示例,DBObject可能看起来像这样:

DBObject metaData = new BasicDBObject();
metaData.put("user", "alex");

GridFS使用两个集合来存储文件元数据及其内容。文件的元数据存储在files集合中,文件的内容存储在chunks集合中。这两个集合都以fs为前缀。

如果我们执行MongoDB命令db[‘fs.files’].find(),我们将看到fs.files集合:

{
    "_id": ObjectId("5602de6e5d8bba0d6f2e45e4"),
    "metadata": {
        "user": "alex"
    },
    "filename": "test.png",
    "aliases": null,
    "chunkSize": NumberLong(261120),
    "uploadDate": ISODate("2015-09-23T17:16:30.781Z"),
    "length": NumberLong(855),
    "contentType": "image/png",
    "md5": "27c915db9aa031f1b27bb05021b695c6"
}

命令db[‘fs.chunks’].find()检索文件的内容:

{
    "_id" : ObjectId("5602de6e5d8bba0d6f2e45e4"),
    "files_id" : ObjectId("5602de6e5d8bba0d6f2e45e4"),
    "n" : 0,
    "data" : 
    { 
        "$binary" : "/9j/4AAQSkZJRgABAQAAAQABAAD/4QAqRXhpZgAASUkqAAgAAAABADEBAgAHAAAAGgAAAAAAAABHb29nbGUAAP/bAIQAAwICAwICAwMDAwQDAwQFCAUFBAQFCgcHBggM
          CgwMCwoLCw0OEhANDhEOCwsQFhARExQVFRUMDxcYFhQYEhQVFAEDBAQGBQUJBgYKDw4MDhQUFA8RDQwMEA0QDA8VDA0NDw0MDw4MDA0ODxAMDQ0MDAwODA8MDQ4NDA0NDAwNDAwQ/8AA
          EQgAHAAcAwERAAIRAQMRAf/EABgAAQEBAQEAAAAAAAAAAAAAAAgGBwUE/8QALBAAAgEDAgQFAwUAAAAAAAAAAQIDBAURBiEABwgSIjFBYXEyUYETFEKhw
          f/EABoBAAIDAQEAAAAAAAAAAAAAAAMEAQIFBgD/xAAiEQACAgEDBAMAAAAAAAAAAAAAAQIRAyIx8BJRYYETIUH/2gAMAwEAAhEDEQA/AHDyq1Bb6GjFPMAszLkZHHCTi1I6O
          cXOFRZ1ZqoX6aqzRClkhb9MGVh2SsNyVI/hjG5389tuGcUaLK1GmFfpn5r3rnXpfV82pGtS3a0XmaGOO3zguKV1SWDwBQDH2uUWTOWMZzuM8bS0VQtJKRb2li9LL3l+4VNQPEfQTOB/WO
          G1K0JtUzwad1eZaYBiqzL4S2N8cZUsa7DqlRGdWvMq5aX6b9Tvb5pIZbggt7VcU3YacSkDbfuLNuu3lkk+98GNfIrLt2gK9K/NWl5Z87Ldebj3R0NTa2tVVKhOI0KoQ5AG4DRqSPk+gHGn
          khUPYNOx92vW9PcrdDW0FUJqOp7po5ETIYMxOdyOAK0qAvcgKPWa0oMTo7SEYDKPp98/5wPoJsx3rZ1wLhojS9iinLD9w9W47iSwVe0Z3wfrPoce2eC4I6rCX9MxrpUpWqudNunUosNLR1EkiyIGDqUKF
          fyZB+AeG80riueQdVfObC/tN1pLdaLfSxMiRQ08aIg2CjtGAB9uEyCSqSWujICUXwghT57A5+ePEoMvUdc5a3XlSsgUhZGjGM/TGAqjz+SfuT7DDmGC6WzzeyOv0+2amOrr3KylzTUwjjDeWGbJJ9/COI
          yvRFFv1iRsVGDaqYGWVsIoBZydsDhQGf/Z", 
        "$type" : "00" 
    }
}

3.2 findOne

findOne只返回一个满足指定查询条件的文档。

String id = "5602de6e5d8bba0d6f2e45e4";
GridFSFile gridFsFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(id)));

上面的代码将返回在上面的示例中添加的结果记录。如果数据库包含多个与查询匹配的记录,则它将只返回一个文档。返回的具体记录将根据自然顺序(文档在数据库中存储的顺序)进行选择。

3.3 find

find从集合中选择文档并将光标返回到所选文档。

假设我们有以下数据库,包含2条记录:

[
    {
        "_id" : ObjectId("5602de6e5d8bba0d6f2e45e4"),
        "metadata" : {
            "user" : "alex"
        },
        "filename" : "test.png",
        "aliases" : null,
        "chunkSize" : NumberLong(261120),
        "uploadDate" : ISODate("2015-09-23T17:16:30.781Z"),
        "length" : NumberLong(855),
        "contentType" : "image/png",
        "md5" : "27c915db9aa031f1b27bb05021b695c6"
    },
    {
        "_id" : ObjectId("5702deyu6d8bba0d6f2e45e4"),
        "metadata" : {
            "user" : "david"
        },
        "filename" : "test.png",
        "aliases" : null,
        "chunkSize" : NumberLong(261120),
        "uploadDate" : ISODate("2015-09-23T17:16:30.781Z"),
        "length" : NumberLong(855),
        "contentType" : "image/png",
        "md5" : "27c915db9aa031f1b27bb05021b695c6"
    }
]

如果我们使用GridFsTemplate执行以下查询:

List<GridFSFile> fileList = new ArrayList<GridFSFile>();
gridFsTemplate.find(new Query()).into(fileList);

由于我们没有提供任何条件,因此生成的列表应包含两条记录。

当然,我们可以为find方法提供一些条件。例如,如果我们想要获取其元数据(metadata)包含名为alex的用户的文件,则代码为:

List<GridFSFile> gridFSFiles = new ArrayList<GridFSFile>();
gridFsTemplate.find(new Query(Criteria.where("metadata.user").is("alex"))).into(gridFSFiles);

结果列表将只包含一条记录。

3.4 delete

delete从集合中删除文档。

使用上一个示例中的数据库,假设我们有以下代码:

String id = "5702deyu6d8bba0d6f2e45e4";
gridFsTemplate.delete(new Query(Criteria.where("_id").is(id)));

执行delete后,数据库中只剩下一条记录:

{
    "_id" : ObjectId("5702deyu6d8bba0d6f2e45e4"),
    "metadata" : {
        "user" : "alex"
    },
    "filename" : "test.png",
    "aliases" : null,
    "chunkSize" : NumberLong(261120),
    "uploadDate" : ISODate("2015-09-23T17:16:30.781Z"),
    "length" : NumberLong(855),
    "contentType" : "image/png",
    "md5" : "27c915db9aa031f1b27bb05021b695c6"
}

以及chunks:

{
    "_id" : ObjectId("5702deyu6d8bba0d6f2e45e4"),
    "files_id" : ObjectId("5702deyu6d8bba0d6f2e45e4"),
    "n" : 0,
    "data" : 
    { 
        "$binary" : "/9j/4AAQSkZJRgABAQAAAQABAAD/4QAqRXhpZgAASUkqAAgAAAABADEBAgAHAAAAGgAAAAAAAABHb29nbGUAAP/bAIQAAwICAwICAwMDAwQDAwQFCAUFBAQFCgcHBggM
          CgwMCwoLCw0OEhANDhEOCwsQFhARExQVFRUMDxcYFhQYEhQVFAEDBAQGBQUJBgYKDw4MDhQUFA8RDQwMEA0QDA8VDA0NDw0MDw4MDA0ODxAMDQ0MDAwODA8MDQ4NDA0NDAwNDAwQ/8AA
          EQgAHAAcAwERAAIRAQMRAf/EABgAAQEBAQEAAAAAAAAAAAAAAAgGBwUE/8QALBAAAgEDAgQFAwUAAAAAAAAAAQIDBAURBiEABwgSIjFBYXEyUYETFEKhw
          f/EABoBAAIDAQEAAAAAAAAAAAAAAAMEAQIFBgD/xAAiEQACAgEDBAMAAAAAAAAAAAAAAQIRAyIx8BJRYYETIUH/2gAMAwEAAhEDEQA/AHDyq1Bb6GjFPMAszLkZHHCTi1I6O
          cXOFRZ1ZqoX6aqzRClkhb9MGVh2SsNyVI/hjG5389tuGcUaLK1GmFfpn5r3rnXpfV82pGtS3a0XmaGOO3zguKV1SWDwBQDH2uUWTOWMZzuM8bS0VQtJKRb2li9LL3l+4VNQPEfQTOB/WO
          G1K0JtUzwad1eZaYBiqzL4S2N8cZUsa7DqlRGdWvMq5aX6b9Tvb5pIZbggt7VcU3YacSkDbfuLNuu3lkk+98GNfIrLt2gK9K/NWl5Z87Ldebj3R0NTa2tVVKhOI0KoQ5AG4DRqSPk+gHGn
          khUPYNOx92vW9PcrdDW0FUJqOp7po5ETIYMxOdyOAK0qAvcgKPWa0oMTo7SEYDKPp98/5wPoJsx3rZ1wLhojS9iinLD9w9W47iSwVe0Z3wfrPoce2eC4I6rCX9MxrpUpWqudNunUosNLR1EkiyIGDqUKF
          fyZB+AeG80riueQdVfObC/tN1pLdaLfSxMiRQ08aIg2CjtGAB9uEyCSqSWujICUXwghT57A5+ePEoMvUdc5a3XlSsgUhZGjGM/TGAqjz+SfuT7DDmGC6WzzeyOv0+2amOrr3KylzTUwjjDeWGbJJ9/COI
          yvRFFv1iRsVGDaqYGWVsIoBZydsDhQGf/Z", 
        "$type" : "00" 
    }
}

3.5 getResources

getResources返回所有具有给定文件名模式的GridFsResource。

假设我们在数据库中有如下记录:

[
    {
        "_id" : ObjectId("5602de6e5d8bba0d6f2e45e4"),
        "metadata" : {
            "user" : "alex"
        },
        "filename" : "test.png",
        "aliases" : null,
        "chunkSize" : NumberLong(261120),
        "uploadDate" : ISODate("2015-09-23T17:16:30.781Z"),
        "length" : NumberLong(855),
        "contentType" : "image/png",
        "md5" : "27c915db9aa031f1b27bb05021b695c6"
    },
    {
        "_id" : ObjectId("5505de6e5d8bba0d6f8e4574"),
        "metadata" : {
            "user" : "david"
        },
        "filename" : "test.png",
        "aliases" : null,
        "chunkSize" : NumberLong(261120),
        "uploadDate" : ISODate("2015-09-23T17:16:30.781Z"),
        "length" : NumberLong(855),
        "contentType" : "image/png",
        "md5" : "27c915db9aa031f1b27bb05021b695c6"
    }, 
    {
        "_id" : ObjectId("5777de6e5d8bba0d6f8e4574"),
        "metadata" : {
            "user" : "tuyucheng"
        },
        "filename" : "tuyucheng.png",
        "aliases" : null,
        "chunkSize" : NumberLong(261120),
        "uploadDate" : ISODate("2015-09-23T17:16:30.781Z"),
        "length" : NumberLong(855),
        "contentType" : "image/png",
        "md5" : "27c915db9aa031f1b27bb05021b695c6"
    }
]

现在让我们使用文件模式执行getResources:

GridFsResource[] gridFsResource = gridFsTemplate.getResources("test*");

这将返回文件名以“test”开头的两条记录(在本例中,它们都被命名为test.png)。

4. GridFSFile核心方法

GridFSFile API也非常简单:

  • getFilename:获取文件的文件名
  • getMetaData:获取给定文件的元数据
  • containsField:确定文档是否包含具有给定名称的字段
  • get:按名称从对象中获取一个字段
  • getId:获取文件的ObjectID
  • keySet:获取对象的字段名称

5. 总结

在本文中,我们了解了MongoDB的GridFS功能,以及如何使用Spring Data MongoDB与它们进行交互。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章