GridFS 是用于存储和检索超过 BSON -文档大小限制为 16 MB 的文件的规范。
在 MongoDB4.0 版本之前只支持单文档事务操作,在 4.0 版本之后开始支持多文档事务操作,但是 GridFS 不支持多文档事务。
GridFS 不是将文件存储在单个文档中,而是将文件分成多个部分或块[1],并将每个块存储为单独的文档。
默认情况下,GridFS 使用默认的块大小 255 kB; 也就是说,GridFS 将文件分成 255 kB 的块,但最后一个块除外。最后一个块只有必要的大小。类似地,不大于块大小的文件只有最终块。
GridFS 使用两个集合来存储文件。一个集合存储文件块,另一个存储文件元数据。
mongod
实例和工具中。不支持原子方式更新整个文件的内容
chunks
存储二进制块files
存储文件的元数据chunks 集合中的每个文档代表 GridFS 中表示的文件的不同块。此集合中的文档具有以下形式:
{
"_id": <ObjectId>, // 文档ID,唯一标识
"files_id": <ObjectId>, // 对应fs.files文档的ID
"n": <num>, // 序号,标识文件的第几个 chunk
"data": <binary> // 文件二级制数据
}
files
集合中的每个文档都代表 GridFS 中的一个文件 。
{
"_id": <ObjectId>, // 文档ID,唯一标识
"chunkSize": <num>, // chunk大小 256kb
"uploadDate": <timetamp>, //文件上传时间
"length": <num>, // 文件长度
"md5": <string>, // 文件md5值
"filename": <string>, // 文件名
"contentType": <string>,// 文件的MIME类型
"metadata": <dataObject>// 文件自定义信息
}
chunks
和files
集合上的索引来提高效率。符合 GridFS 规范的驱动程序会自动创建这些索引以方便使用。
mongofiles
实用程序可以从命令行操作 GridFS 对象中存储在 MongoDB 实例中的文件mongofiles <options> <commands> <filename>
要使用该选项连接到mongod
强制授权--auth
,您必须使用 --username
和--password
选项。连接用户必须至少拥有:
read
使用时,为访问数据库角色 list
,search
或get
命令,readWrite
使用put
或delete
命令时访问的数据库的角色。 mongofiles -u "writeUser" -p "passwd" --authenticationDatabase admin
list <prefix>
列出 GridFS 存储中的文件。list
(例如<prefix>
)之后指定的字符 可选地将返回项的列表限制为以该字符串开头的文件。
search <string>
列出 GridFS 存储中的文件,其名称与任何部分匹配<string>
。
put <filename>
将指定文件从本地文件系统复制到 GridFS 存储中。这里,<filename>
指的是对象在 GridFS 中的名称,并mongofiles
假设它反映了文件在本地文件系统上的名称。如果本地文件名不同,请使用该选项。mongofiles --local
get <filename>
将指定文件从 GridFS 存储复制到本地文件系统。这里,<filename>
指的是对象在 GridFS 中的名称。mongofiles
使用filename
GridFS 中的文件将文件写入本地文件系统。要在本地文件系统上为文件选择其他位置,请使用该 --local
选项。
get_id "<_id>"
*版本 3.2.0 中的新功能。*将其指定的文件<_id>
从 GridFS 存储复制到本地文件系统。这里<_id>
指的是_id
GridFS 中对象的扩展 JSON :从 MongoDB 4.2 开始,get_id
可以接受 ObjectId 值或非 ObjectId 值<_id>
。在 MongoDB 4.0 及更早版本中,get_id
只接受<ObjectId>
值。mongofiles
使用filename
GridFS 中的文件将文件写入本地文件系统。要在本地文件系统上为文件选择其他位置,请使用该 --local
选项。
delete <filename>
从 GridFS 存储中删除指定的文件。
delete_id "<_id>"
版本 3.2.0 中的新功能。<_id>
从 GridFS 存储中删除由其指定的文件:从 MongoDB 4.2 开始,delete_id
可以接受 ObjectId 值或非 ObjectId 值<_id>
。在 MongoDB 4.0 及更早版本中,delete_id
只接受<ObjectId>
值。
--host --port
--username 与
--authenticationDatabase`
--db`` <database>``, ``-d`` <database>
--uri
--uri "mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"
以下命令行选项不能与--uri
选项一起使用:
--host
--port
--db
--username
--password
(如果 URI 连接字符串也包含密码)--authenticationDatabase
--authenticationMechanism
而是将这些选项指定为--uri
连接字符串的一部分。
--local<filename>
, -l
指定 get 和 put 操作的文件的本地文件系统名称。
在mongofiles put和mongofiles 获取命令中,必需的<filename>
修饰符指的是对象在 GridFS 中的名称。mongofiles
假设这反映了本地文件系统上的文件名。此设置将覆盖此默认值。
在系统的 shell 中运行命令
创建一个可读写的用户 writeUser
上传文件 js-yaml.js 到数据库 files 中
mongofiles -u "writeUser" -p "passwd" --authenticationDatabase admin -d files put js-yaml.js
返回信息
2019-08-23T14:09:05.642+0000 connected to: mongodb://localhost/
2019-08-23T14:09:05.684+0000 added gridFile: js-yaml.js
第二次上传相同文件到数据库中,GridFS 不会对其进行覆盖,会存储两个相同文件
查看数据库 files 中的文件列表
mongofiles -u "writeUser" -p "passwd" --authenticationDatabase admin -d files list
返回信息
2019-08-23T14:17:46.973+0000 connected to: mongodb://localhost/
js-yaml.js 108442
SpringBoot 导入 mongo,web-start,io 模块(2.1.7.RELEASE)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
application.properties 中
spring.data.mongodb.uri=mongodb://192.168.191.156:27017/files
在 springboot 启动类同级目录或者子目录下创建 GridFSApi.java
package com.yufh.springbootmongofiles;
import com.mongodb.client.gridfs.model.GridFSFile;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Controller;
import java.io.*;
/**
* @author yufh
* @describe
* @date 2019/8/23
*/
@Controller
public class GridFSApi {
@Autowired
private GridFsTemplate gridFsTemplate;
//上传文件
public void upload() {
File file = new File("D:\\1.jpg");
try {
ObjectId id = gridFsTemplate.store(new FileInputStream(file), file.getName());
System.out.println(id);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//下载文件
public void download( String fileName) throws IOException {
Query query = Query.query(Criteria.where("filename").is(fileName));
GridFSFile mongoFile = gridFsTemplate.findOne(query);
//gridfs下载在2.x以下版本为
// try {
// mongoFile.writeTo("D:\\a.txt");
// } catch (IOException e) {
// e.printStackTrace();
// }
//2.x以上版本。find的返回值进行了更改。
GridFsResource gs = gridFsTemplate.getResource(mongoFile);
OutputStream os = null;
try {
byte[] bs = new byte[1024];
int len;
InputStream in = gs.getInputStream();
File file = new File("D:\\");
os = new FileOutputStream(file.getPath() + File.separator + gs.getFilename());
while ((len = in.read(bs)) != -1) {
os.write(bs, 0, len);
}
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
os.close();
}
}
//删除文件
public void delete( String fileName) {
Query query = Query.query(Criteria.where("filename").is(fileName));
gridFsTemplate.delete(query);
}
}
test 测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootMongofilesApplicationTests {
@Resource GridFSApi gridFSApi;
@Test
public void downloadFiles() throws IOException {
gridFSApi.download("a.txt");
}
@Test
public void uploadFiles() {
gridFSApi.upload();
}
@Test
public void deleteFiles() {
gridFSApi.delete("1.jpg");
}
}
GridFs 不会自动处理 md5 值相同的文件,也就是说,同一个文件进行两次 put 命令,将会在 GridFS 中对应两个不同的存储,对于存储来说,这是一种浪费。对于 md5 相同的文件,如果想要在 GridFS 中只有一个存储,需要通过 API 进行扩展处理。
MongoDB 不会释放已经占用的硬盘空间。即使删除 db 中的集合 MongoDB 也不会释放磁盘空间。同样,如果使用 GridFS 存储文件,从 GridFS 存储中删除无用的垃圾文件,MongoDB 依然不会释放磁盘空间的。这会造成磁盘一直在消耗,而无法回收利用的问题。
当使用 db.repairDatabase()命令没有足够的磁盘剩余空间时,可以采用 dump & restore 方式回收磁盘资源。如果 MongoDB 是副本集模式,dump & restore 方式可以做到对外持续服务,在不影响 MongoDB 正常使用下回收磁盘资源。
MogonDB 使用副本集, 实践使用 dump & restore 方式,回收磁盘资源。70G 的数据在 2 小时之内完成数据清理及磁盘回收,并且整个过程不影响 MongoDB 对外服务,同时可以保证处理过程中数据库增量数据的完整。
参考链接:http://rdc.hundsun.com/portal/article/703.html