meface/docs/article/db/mongodb_gridfs.md

19 KiB
Raw Blame History

title date author tags categories
MongoDB GridFS文件存储 2021-01-28 ac
MongoDB
GridFS
mongofiles
Database

GIS中遥感影像数据可以使用MongoDB的GridFS进行存储。

1 GridFS 简介

GridFS文件系统是MongoDB存储大文件的一种规范,所有官方的MongoDB驱动都遵循该规范。它解决了BSON文档大小不能超过16M的问题。

GridFS不是将文件存储在单个文档中,而是将文件分成多个数据块,并将每个块存储为单独的文档。默认情况下,GridFS使用的默认数据块大小为255 kB;也就是说,除了最后一个数据块外,GridFS将一个文件划分为255 kB的小块。

GridFS使用两个集合来存储文件。一个集合存储文件的数据块,另一个集合存储文件元数据,像文件名称、大小、创建日期等。

当您在使用GridFS查询文件时,会根据需要重新组装这些数据块。另外GridFS不仅可以存储操过16MB的文件还可以访问大文件的部分信息而不必将整个文件加载到内存中。

使用GridFS的方式有以下两种:

  • 使用程序接口的方式,利用MongoDB对各程序语言的提供的驱动对文件进行操作。

    1614838692793

  • 使用mongofiles命令行工具。

2 GridFS 集合

GridFS将文件存储在两个集合中:

  • chunks:存储二进制块
  • files:存储文件的元数据

GridFS通过在每个集合前面加上前缀将这两个集合放在一个公共容器bucket命名空间中。默认情况下GridFS使用两个集合,一个容器名为fs:

  • fs.files
  • fs.chunks

官方的英语文档描述的更加形象The two collections are in a common bucket and the collection names are prefixed with the bucket name.

2.1 chunks集合

{
  "_id" : <ObjectId>,
  "files_id" : <ObjectId>,
  "n" : <num>,
  "data" : <binary>
}

ObjectId: 一种特殊的BSON类型它保证集合内的唯一性。

ObjectId值的长度为12个字节包括: 4 byte 时间戳、5 byte 随机数 、3 byte 增长量

  • chunks._id数据块的标识
  • chunks.files_id所属文件的标识
  • chunks.n 块的序列号。GridFS给所有块编号从0开始。
  • chunks.data 数据块中装载的数据Binary类型。

2.2 files集合

{
  "_id" : <ObjectId>,
  "length" : <num>,
  "chunkSize" : <num>,
  "uploadDate" : <timestamp>,
  "md5" : <hash>,
  "filename" : <string>,
  "contentType" : <string>,
  "aliases" : <string array>,
  "metadata" : <any>,
}
  • files._id文件标识
  • files.length文件长度
  • files.chunkSize 每个块的大小(以字节为单位)。GridFS将文档分成大小为chunkSize的块但最后一个块的大小仅根据需要而定。默认大小是255kb。
  • files. uploadDate 文件第一次被GridFS存储的日期。该值具有日期类型。
  • files.md5 MD5算法被FIPS 140-2禁止。MongoDB驱动程序已弃用MD5支持并将在未来版本中删除MD5生成。需要文件摘要的应用程序应该在GridFS之外实现它并存储在files.metadata中。
  • files.filename 可读的GridFS文件名称。
  • files.metadata 可选的。元数据字段可以是任何数据类型,可以保存您想要存储的任何附加信息。

3 mongofiles工具

3.1 mongofiles作用

mongofiles工具可以通过命令行操作MongoDB实例中GridFS对象中存储的文件。它提供了存储在文件系统和GridFS中的对象之间的接口。

MongoDB4.4 开始,mongofilesMongoDB服务器单独发布并使用自己的版本控制初始版本为100.0.0。在此之前,mongofiles是与MongoDB一起发布的,并使用了匹配的版本控制。

3.2 安装 mongofiles

windows环境

根据之前安装MongoDB实例,选择下载工具的版本。之前MongoDB使用的是zip免安装的形式,所以这里下载mongodb-database-tools-windows-x86_64-100.2.zip

image-20210128151549082

image-20210128152329012

image-20210128153732500

image-20210128153418405

image-20210128153920377

将下载的文件解压到任意位置,再将mongofiles工具目录下的bin目录添加到系统的Path环境变量中。

Linux环境ubuntu

qiusj@u20:~$ cat /etc/os-release
NAME="Pop!_OS"
VERSION="20.04 LTS"
ID=pop
ID_LIKE="ubuntu debian"
PRETTY_NAME="Pop!_OS 20.04 LTS"
VERSION_ID="20.04"
HOME_URL="https://pop.system76.com"
SUPPORT_URL="https://support.system76.com"
BUG_REPORT_URL="https://github.com/pop-os/pop/issues"
PRIVACY_POLICY_URL="https://system76.com/privacy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
LOGO=distributor-logo-pop-os
#查看Linux内核版本
qiusj@u20:~$ arch
x86_64

下载合适的版本,可以下载后使用sftp传输到Linux也可以使用curl命令下载:

image-20210128164131790

$ curl -o mongodb-database-tools.tgz https://fastdl.mongodb.org/tools/db/mongodb-database-tools-ubuntu2004-x86_64-100.2.1.tgz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 64.0M  100 64.0M    0     0  1222k      0  0:00:53  0:00:53 --:--:-- 2106k

$ ll
总用量 128560
drwxr-xr-x  5 qiusj intplanet     4096 1月  28 17:00 ./
drwxr-xr-x  6 qiusj intplanet     4096 1月   5 09:42 ../
-rw-r--r--  1 qiusj intplanet 67211368 1月  28 17:00 mongodb-database-tools.tgz

#解压
$ tar -zxvf mongodb-database-tools.tgz 
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/LICENSE.md
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/README.md
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/THIRD-PARTY-NOTICES
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/bin/bsondump
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/bin/mongodump
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/bin/mongoexport
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/bin/mongofiles
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/bin/mongoimport
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/bin/mongorestore
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/bin/mongostat
mongodb-database-tools-ubuntu2004-x86_64-100.2.1/bin/mongotop

#将解压的文件bin目录下的命令复制到/usr/local/bin/目录下
$ cd mongodb-database-tools-ubuntu2004-x86_64-100.2.1/bin
$ sudo cp * /usr/local/bin
$ mongofiles --version
mongofiles version: 100.2.1
git version: 0cb6f592fcb425ee5b3d5540341de75531d28dac
Go version: go1.12.17
   os: linux
   arch: amd64
   compiler: gc

3.3 基本使用

mongofiles命令的语法格式如下:

mongofiles <options> <connection-string> <command> <filename or _id>
  • options主要配置mongofiles的一些读写优先级。
  • connection-string是连接mongod/mongos的配合信息如host、port、安全认证等相关配置。
  • commandmongofiles具体的文件操作,如导入、导出、查询等。

对于副本集,mongofiles只能从主节点读。

当启动安全认证后,mongofiles连接的用户需要有read权限(listearchget等命令)和readWrite权限(putdelete等命令)

常用Options配置项

options description
--help 返回 mongofiles 的配置项和使用信息
--version 返回 mongofiles 的版本信息
--uri 指定MongoDB 的连接信息,如:
--uri="mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"
--config 指定配置文件,以配置文件的形式启动

**Commands: **

命令 说明
list <prefix> 列出GridFS存储的文件。<prefix>用于筛选返回列表中以 prefix字符串为前缀的文件。
search <string> 列出GridFS中名称与<string>的任何部分匹配的文件。
put <filename1[ filename2] ...> 将指定的文件从本地文件系统复制到GridFS中。多个文件可以指定为空格分隔的列表。
get <filename1[ filename2] ...> 将指定的文件从GridFS存储复制到本地文件系统。
get_id "<_id>" 将文件(由<_id>指定)从GridFS中复制到本地文件系统。<_id>是该对象在GridFS中的扩展JSON _id。
get_regex <regex> --regexOptions <regex-options> 将匹配指定的<regex>表达式的一个或多个文件从GridFS复制到本地文件系统。get_regex命令使用Perl兼容的正则表达式(“PCRE”)8.42版本支持UTF-8。
delete <filename> GridFS中删除指定的文件
delete_id "<_id>" GridFS中删除由<_id>指定的文件。

3.4 示例

在系统的命令行中运行 mongofiles命令:

# 查看本地mongofiles实例执行过的历史命令
mongofiles -d=records list

导入文件,下面命令mongofiles 实例将连接 uri中的数据库,将文件存入GridFS中。

$ mongofiles --uri=mongodb://192.168.0.152:27017 put /home/qiusj/.local/mongodb/mongofiles/data/shenzhen_CholophyII_2020.tif
2021-03-04T17:18:06.681+0800    connected to: mongodb://192.168.0.152:27017
2021-03-04T17:18:06.682+0800    adding gridFile: /home/qiusj/.local/mongodb/mongofiles/data/shenzhen_CholophyII_2020.tif

2021-03-04T17:18:06.824+0800    added gridFile: /home/qiusj/.local/mongodb/mongofiles/data/shenzhen_CholophyII_2020.tif

查看集合:

#连接数据库
$ /usr/local/mongodb/bin/mongo --host=192.168.0.152 --port=27017
#查看数据库
> db
test
#查看集合
> show collections
fs.chunks
fs.files

#查看files集合
> db.fs.files.find()
{ 
  "_id" : ObjectId("6040a5ce6211dcaf818ac9aa"), 
  "length" : NumberLong(15838376), 
  "chunkSize" : 261120, 
  "uploadDate" : ISODate("2021-03-04T09:18:06.824Z"), 
  "filename" : "/home/qiusj/.local/mongodb/mongofiles/data/shenzhen_CholophyII_2020.tif", 
  "metadata" : {  } 
}

查询文件:

$ mongofiles search 'Cho'
2021-03-04T17:48:10.976+0800    connected to: mongodb://localhost/
/home/qiusj/.local/mongodb/mongofiles/data/shenzhen_CholophyII_2020.tif 15838376

搜索结果filename length

可以发现mongofiles命令默认连接本地的 mongo 服务,如果不是本地需带上 uri 连接信息。

获取文件:

$ mongofiles --uri=mongodb://localhost:27017 get /home/qiusj/.local/mongodb/mongofiles/data/shenzhen_CholophyII_2020.tif --local=test.tif
2021-03-04T17:57:34.507+0800    connected to: mongodb://localhost:27017
2021-03-04T17:57:34.531+0800    finished writing to test.tif

get <filename1[ filename2] ...>命令,当只有一个文件时,可以使用--local参数给文件重新命名。

导出的目录为get命令执行所在的当前目录。

删除文件:

$ mongofiles delete /home/qiusj/.local/mongodb/mongofiles/data/shenzhen_CholophyII_2020.tif
2021-03-04T18:03:23.399+0800    connected to: mongodb://localhost/
2021-03-04T18:03:23.402+0800    successfully deleted all instances of '/home/qiusj/.local/mongodb/mongofiles/data/shenzhen_CholophyII_2020.tif' from GridFS

#再去数据库里查看
$ /usr/local/mongodb/bin/mongo
> show collections
fs.chunks
fs.files
> db.fs.files.find()
>

4 Springboot中使用GridFS

使用一个单实例的未启用认证的mongodb作为示例数据库。

4.1 导入依赖

compile 'org.springframework.boot:spring-boot-starter-data-mongodb'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

4.2 配置文件

application.yml文件:

#如果连接的不是本地的mongo会比较慢因为会受到网络传输速度的影响
spring:
  data:
    mongodb:
      database: files
      host: localhost
      port: 27017

#用于测试的本地遥感数据文件
rsfilepath: "E:\\data\\ShenzhenNDWI_2019_S2L1C.tif"

MongoConfig.java,在该类中根据配置信息创建mongo的客户端实例和GridFSBucket实例。

GridFS用于存储文件的两个集合都在同一个bucket中。集合的前缀为bucket name,默认为fs

@Configuration
public class MongoConfig {

    @Value("${spring.data.mongodb.host}")
    private String host;

    @Value("${spring.data.mongodb.port}")
    private int port;

    @Value("${spring.data.mongodb.database}")
    private String database;

    @Bean
    public MongoClient getMongoClient(){
        MongoClient mongoClient =  MongoClients.create(
                MongoClientSettings.builder()
                        .applyToClusterSettings(builder ->
                                builder.hosts(Arrays.asList(
                                        new ServerAddress(host, port)
                                       )))
                        .build());
        return mongoClient;
    }

    @Bean
    public GridFSBucket getgridFSBucket(MongoClient mongoClient) {
        MongoDatabase mongoDatabase = mongoClient.getDatabase(database);
        //使用默认的fs前缀
        GridFSBucket gridFSBucket = GridFSBuckets.create(mongoDatabase);
        return gridFSBucket;
    }
}

4.3 测试类

我们在Test模块下创建一个GridsApplicationTests.java测试类,用于测试GridFSBucket中的方法:

1615447216815


@SpringBootTest
class GridfsApplicationTests {

    @Autowired
    private GridFSService gridFSService;

    @Value("${rsfilepath}")
    private String rsfilepath;

    private int chunckSize = 1024;

    @Autowired
    private GridFSBucket gridFSBucket;

    /**
     * uploadFromStream()方法
     * 文件导入到GridFS中
     */
    @Test
    void testUploadToGridFS() throws IOException {
        File file = new File(rsfilepath);
        FileInputStream inputStream = new FileInputStream(file);

        //GridFSUploadOptions配置chunkSize和添加元数据
        GridFSUploadOptions options = new GridFSUploadOptions()
                .chunkSizeBytes(chunckSize)
                .metadata(new Document("k1","v1").append("k2","v2"));

        //GridFSBucket.InputStream方法读取字节输入流中的内容并将其保存到GridFSBucket中
        ObjectId fileId = gridFSBucket.uploadFromStream("GridFS输入", inputStream, options);
        System.out.println(fileId);
        inputStream.close();
    }

    /**
     * openUploadStream()方法
     * 返回GridFSUploadStream作为缓存
     * 文件导入到GridFS中
     */
    @Test
    void testUOpenploadToGridFS() throws IOException {
        byte[] data = Files.readAllBytes(Paths.get(rsfilepath));

        //GridFSUploadOptions 配置 chunkSize 和添加元数据
        GridFSUploadOptions options = new GridFSUploadOptions()
                .chunkSizeBytes(chunckSize)
                .metadata(new Document("format","tiff").append("SRS","EPSG:3857"));

        GridFSUploadStream uploadStream = gridFSBucket.openUploadStream("缓冲", options);
        //GridFSUploadStream缓冲数据达到chunckSizeBytes时才进行插入数据块chunks操作
        uploadStream.write(data);
        //当GridFSUploadStream被关闭后才会将最后一块chunk写入chunks集合和将文件元数据插入files集合
        uploadStream.close();
        String fileId = uploadStream.getObjectId().toString();
        System.out.println(fileId);
    }

    /**
     * find()方法
     * 查找GridFS中的文件
     */
    @Test
    void testFindFileFromGridFS() throws IOException {
        //查询GridFS中所有的文件
        gridFSBucket.find().forEach(gridFSFile -> {
            System.out.println(gridFSFile.getObjectId()+": "+gridFSFile.getFilename());
        });
        System.out.println("-----------------------");
        gridFSBucket.find(eq("metadata.SRS","EPSG:3857")).forEach(gridFSFile -> {
            System.out.println(gridFSFile.getObjectId()+": "+gridFSFile.getFilename());
        });
    }

    /**
     * downloadToStream()方法
     * 根据fileId从GridFS中下载文件
     */
    @Test
    void testDownloadToStreamById() throws IOException {
        //需要导出的文件的fileId
        ObjectId fileId = new ObjectId("6049d240efdc5715fc6ecbbe");
        //指定输出目标路径和输出文件名
        File file = new File("E:\\data\\out\\NDWI_2019_S2L1C.tiff");
        if(!file.exists()){
            file.getParentFile().mkdirs();
            file.createNewFile();
        }
        //创建文件字节输出流
        FileOutputStream out = new FileOutputStream(file);

        gridFSBucket.downloadToStream(fileId, out);
        out.close();
    }

    /**
     * downloadToStream()方法
     * 根据filename从GridFS中下载文件
     */
    @Test
    void testDownloadToStreamByFilename() throws IOException {
        //创建下载条件对象
        GridFSDownloadOptions downloadOptions = new GridFSDownloadOptions();
        /**
         * 版本控制默认下载最新的一个版本对象这里指定为0选择原始的版本
         * 0 = the original stored file
         * 1 = the first revision
         * 2 = the second revision
         * etc..
         * -2 = the second most recent revision
         * -1 = the most recent revision
         */
        downloadOptions.revision(0);

        //指定输出目标路径和输出文件名
        File file = new File("E:\\data\\out\\NDWI2.tiff");
        if(!file.exists()){
            file.getParentFile().mkdirs();
            file.createNewFile();
        }
        //创建文件字节输出流
        FileOutputStream out = new FileOutputStream(file);

        gridFSBucket.downloadToStream("归一化指数2019",out,downloadOptions);
        out.close();
    }


    /**
     * rename,重命名
     */
    @Test
    void testRenameFile(){
        ObjectId fileId = new ObjectId("6049d240efdc5715fc6ecbbe");
        gridFSBucket.rename(fileId,"归一化指数2019");
    }

    /**
     * delete
     */
    @Test
    void testDeleteFile(){
        ObjectId fileId = new ObjectId("6049bf658ca88237b95c9952");
        gridFSBucket.delete(fileId);
    }
}

参考文章

[1] GridFS https://docs.mongodb.com/manual/core/gridfs/

[2] mongofiles https://docs.mongodb.com/database-tools/mongofiles

[3] Java Driver Tutorial GridFS https://mongodb.github.io/mongo-java-driver/4.2/driver/tutorials/gridfs/