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使用filenameGridFS 中的文件将文件写入本地文件系统。要在本地文件系统上为文件选择其他位置,请使用该 --local选项。
get_id "<_id>"
*版本 3.2.0 中的新功能。*将其指定的文件<_id>从 GridFS 存储复制到本地文件系统。这里<_id>指的是_idGridFS 中对象的扩展 JSON :从 MongoDB 4.2 开始,get_id可以接受 ObjectId 值或非 ObjectId 值<_id>。在 MongoDB 4.0 及更早版本中,get_id只接受<ObjectId>值。mongofiles使用filenameGridFS 中的文件将文件写入本地文件系统。要在本地文件系统上为文件选择其他位置,请使用该 --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