GIS中遥感影像数据可以使用MongoDB的GridFS进行存储。
# 1 GridFS
简介
GridFS
文件系统是MongoDB
存储大文件的一种规范,所有官方的MongoDB
驱动都遵循该规范。它解决了BSON
文档大小不能超过16M的问题。
GridFS
不是将文件存储在单个文档中,而是将文件分成多个数据块,并将每个块存储为单独的文档。默认情况下,GridFS
使用的默认数据块大小为255 kB;也就是说,除了最后一个数据块外,GridFS
将一个文件划分为255 kB的小块。
GridFS
使用两个集合来存储文件。一个集合存储文件的数据块,另一个集合存储文件元数据,像文件名称、大小、创建日期等。
当您在使用GridFS
查询文件时,会根据需要重新组装这些数据块。另外GridFS
不仅可以存储操过16MB的文件,还可以访问大文件的部分信息而不必将整个文件加载到内存中。
使用GridFS
的方式有以下两种:
使用程序接口 (opens new window)的方式,利用
MongoDB
对各程序语言的提供的驱动对文件进行操作。使用
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>
}
2
3
4
5
6
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>,
}
2
3
4
5
6
7
8
9
10
11
- 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
中的对象之间的接口。
从
MongoDB
4.4 开始,mongofiles
从MongoDB
服务器单独发布,并使用自己的版本控制,初始版本为100.0.0。在此之前,mongofiles
是与MongoDB
一起发布的,并使用了匹配的版本控制。
# 3.2 安装 mongofiles
根据之前安装MongoDB
实例,选择下载工具的版本。之前MongoDB
使用的是zip
免安装的形式,所以这里下载mongodb-database-tools-windows-x86_64-100.2.zip
。
将下载的文件解压到任意位置,再将mongofiles
工具目录下的bin
目录添加到系统的Path
环境变量中。
Linux环境(ubuntu) (opens new window)
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
下载合适的版本,可以下载后使用sftp
传输到Linux,也可以使用curl
命令下载:
$ 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 3.3 基本使用
mongofiles
命令的语法格式如下:
mongofiles <options> <connection-string> <command> <filename or _id>
options
主要配置mongofiles
的一些读写优先级。connection-string
是连接mongod/mongos
的配合信息,如host、port、安全认证等相关配置。command
是mongofiles
具体的文件操作,如导入、导出、查询等。
对于副本集,
mongofiles
只能从主节点读。当启动安全认证后,
mongofiles
连接的用户需要有read
权限(list
、earch
、get
等命令)和readWrite
权限(put
、delete
等命令)
常用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
2
导入文件,下面命令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
2
3
4
5
6
查看集合:
#连接数据库
$ /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" : { }
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
查询文件:
$ 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
2
3
搜索结果: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
2
3
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()
>
2
3
4
5
6
7
8
9
10
11
# 4 Springboot中使用GridFS
使用一个单实例的未启用认证的mongodb
作为示例数据库。
# 4.1 导入依赖
compile 'org.springframework.boot:spring-boot-starter-data-mongodb'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
2
# 4.2 配置文件
application.yml
文件:
#如果连接的不是本地的mongo,会比较慢,因为会受到网络传输速度的影响
spring:
data:
mongodb:
database: files
host: localhost
port: 27017
#用于测试的本地遥感数据文件
rsfilepath: "E:\\data\\ShenzhenNDWI_2019_S2L1C.tif"
2
3
4
5
6
7
8
9
10
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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 4.3 测试类
我们在Test模块下创建一个GridsApplicationTests.java
测试类,用于测试GridFSBucket
中的方法:
@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);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# 参考文章
[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/