1. GridFS简介

GridFS是Mongo的一个子模块,使用GridFS可以基于MongoDB来持久存储文件。并且支持分布式应用(文件分布存储和读取)。作为MongoDB中二进制数据存储在数据库中的解决方案,通常用来处理大文件,对于MongoDB的BSON格式的数据(文档)存储有尺寸限制,最大为16M。但是在实际系统开发中,上传的图片或者文件可能尺寸会很大,此时我们可以借用GridFS来辅助管理这些文件。

GridFS不是MongoDB自身特性,只是一种将大型文件存储在MongoDB的文件规范,所有官方支持的驱动均实现了GridFS规范。GridFS制定大文件在数据库中如何处理,通过开发语言驱动来完成、通过API接口来存储检索大文件。

2. GridFS使用场景

(1) 如果您的文件系统在一个目录中存储的文件的数量有限,你可以使用GridFS存储尽可能多的文件。

(2) 当你想访问大型文件的部分信息,却不想加载整个文件到内存时,您可以使用GridFS存储文件,并读取文件部分信息,而不需要加载整个文件到内存。

(3) 当你想让你的文件和元数据自动同步并部署在多个系统和设施,你可以使用GridFS实现分布式文件存储。

3. GridFS存储原理

GridFS使用两个集合(collection)存储文件。一个集合是chunks, 用于存储文件内容的二进制数据;一个集合是files,用于存储文件的元数据。

GridFS会将两个集合放在一个普通的buket中,并且这两个集合使用buket的名字作为前缀。MongoDB的GridFs默认使用fs命名的buket存放两个文件集合。因此存储文件的两个集合分别会命名为集合fs.files ,集合fs.chunks。

当然也可以定义不同的buket名字,甚至在一个数据库中定义多个bukets,但所有的集合的名字都不得超过mongoDB命名空间的限制。

MongoDB集合的命名包括了数据库名字与集合名字,会将数据库名与集合名通过“.”分隔(eg:<database>.<collection>)。而且命名的最大长度不得超过120bytes。

当把一个文件存储到GridFS时,如果文件大于chunksize (每个chunk块大小为256KB),会先将文件按照chunk的大小分割成多个chunk块,最终将chunk块的信息存储在fs.chunks集合的多个文档中。然后将文件信息存储在fs.files集合的唯一一份文档中。其中fs.chunks集合中多个文档中的file_id字段对应fs.files集中文档”_id”字段。

读文件时,先根据查询条件在files集合中找到对应的文档,同时得到“_id”字段,再根据“_id”在chunks集合中查询所有“files_id”等于“_id”的文档。最后根据“n”字段顺序读取chunk的“data”字段数据,还原文件。

4. 存储过程

fs.files 集合存储文件的元数据,以类json格式文档形式存储。每在GridFS存储一个文件,则会在fs.files集合中对应生成一个文档。
fs.files集合中文档的存储内容如下:

fs.chunks 集合存储文件文件内容的二进制数据,以类json格式文档形式存储。每在GridFS存储一个文件,GridFS就会将文件内容按照chunksize大小(chunk容量为256k)分成多个文件块,然后将文件块按照类json格式存在.chunks集合中,每个文件块对应fs.chunk集合中一个文档。一个存储文件会对应一到多个chunk文档。
fs.chunks集合中文档的存储内容如下:

为了提高检索速度 MongoDB为GridFS的两个集合建立了索引。fs.files集合使用是“filename”与“uploadDate” 字段作为唯一、复合索引。fs.chunk集合使用的是“files_id”与“n”字段作为唯一、复合索引。

5. 注意事项

(1) GridFs不会自动处理md5值相同的文件,也就是说,同一个文件进行两次put命令,将会在GridFS中对应两个不同的存储,对于存储来说,这是一种浪费。对于md5相同的文件,如果想要在GridFS中只有一个存储,需要通过API进行扩展处理。

(2) MongoDB 不会释放已经占用的硬盘空间。即使删除db中的集合 MongoDB也不会释放磁盘空间。同样,如果使用GridFS存储文件,从GridFS存储中删除无用的垃圾文件,MongoDB依然不会释放磁盘空间的。这会造成磁盘一直在消耗,而无法回收利用的问题。

如何释放磁盘空间?

(1) 可以通过修复数据库来回收磁盘空间,即在mongo shell中运行db.repairDatabase()命令或者db.runCommand({ repairDatabase: 1 })命令。(此命令执行比较慢)。

使用通过修复数据库方法回收磁盘时需要注意,待修复磁盘的剩余空间必须大于等于存储数据集占用空间加上2G,否则无法完成修复。因此使用GridFS大量存储文件必须提前考虑设计磁盘回收方案,以解决mongoDB磁盘回收问题。

(2) 使用dump & restore方式,即先删除mongoDB数据库中需要清除的数据,然后使用mongodump备份数据库。备份完成后,删除MongoDB的数据库,使用Mongorestore工具恢复备份数据到数据库。

当使用db.repairDatabase()命令没有足够的磁盘剩余空间时,可以采用dump & restore方式回收磁盘资源。如果MongoDB是副本集模式,dump & restore方式可以做到对外持续服务,在不影响MongoDB正常使用下回收磁盘资源。

6. 代码示例

代码基于spring boot,主要实现GridFS的基本操作。

(1) application.properties配置如下:

spring.data.mongodb.uri=mongodb://localhost:27017/test

(2) Spring Boot的启动函数

 package com.ws;

 import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

(3) Spring Boot的domain域,主要定义返回标识

 package com.ws;

 public class Response {
private String name; public Response(String name) {
this.name = name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} }

(4) Spring Boot的Controller层,定义接口函数

 package com.ws;

 import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.gridfs.GridFSDBFile;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
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.GridFsTemplate;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.UUID; @RestController
@RequestMapping("/api")
public class GridFSApi {
private static Logger LOGGER = Logger.getLogger(GridFSApi.class);
@Autowired
private GridFsTemplate gridFsTemplate; @RequestMapping(value = "/save", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public Response save(@RequestParam(value = "file", required = true) MultipartFile file) { LOGGER.info("Saving file..");
DBObject metaData = new BasicDBObject();
metaData.put("createdDate", new Date()); String fileName = UUID.randomUUID().toString(); LOGGER.info("File Name: " + fileName); InputStream inputStream = null;
try {
inputStream = file.getInputStream();
gridFsTemplate.store(inputStream, fileName, "image", metaData);
LOGGER.info("File saved: " + fileName);
} catch (IOException e) {
LOGGER.error("IOException: " + e);
throw new RuntimeException("System Exception while handling request");
}
LOGGER.info("File return: " + fileName);
return new Response(fileName);
} @RequestMapping(value = "/get", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] get(@RequestParam(value = "fileName", required = true) String fileName) throws IOException {
LOGGER.info("Getting file.." + fileName);
List<GridFSDBFile> result = gridFsTemplate
.find(new Query().addCriteria(Criteria.where("filename").is(fileName)));
if (result == null || result.size() == 0) {
LOGGER.info("File not found" + fileName);
throw new RuntimeException("No file with name: " + fileName);
}
LOGGER.info("File found " + fileName);
return IOUtils.toByteArray(result.get(0).getInputStream());
} @RequestMapping(value = "/delete", method = RequestMethod.DELETE)
public void delete(@RequestParam(value = "fileName", required = true) String fileName) {
LOGGER.info("Deleting file.." + fileName);
gridFsTemplate.delete(new Query().addCriteria(Criteria.where("filename").is(fileName)));
LOGGER.info("File deleted " + fileName);
}
}

Spring Boot使用mongo的GridFS模块的更多相关文章

  1. Spring Boot 的 10 个核心模块

    学习 Spring Boot 必须得了解它的核心模块,和 Spring 框架一样,Spring Boot 也是一个庞大的项目,也是由许多核心子模块组成的. 你所需具备的基础 告诉你,Spring Bo ...

  2. Spring Boot + docker +mongo

    启动mongo镜像 docker run --name mongo-container -d -P mongo 连接到容器内 docker exec -it eb sh 输入:mongo 输入:sho ...

  3. Spring Boot分布式系统实践【基础模块构建3.3】注解轻松实现操作日志记录

    日志注解 前言 spring切面的编程,spring中事物处理.日志记录常常与pointcut相结合 * * Pointcut 是指那些方法需要被执行"AOP",是由"P ...

  4. Spring Boot使用MongoDB GridFS进行文件的操作

    1. GridFS简介 GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片.音频.视频等),但是它是存储在MonoDB的集合中. GridFS 会将文件对象分割成多个的ch ...

  5. 系统中异常公共处理模块 in spring boot

    最近在用spring boot 做微服务,所以对于异常信息的 [友好展示]有要求,我设计了两点: 一. 在业务逻辑代码中,异常的抛出 我做了限定,一般只会是三种: 1. OmcException // ...

  6. Spring Boot 多模块与 Maven 私有仓库

    前言 系统复杂了,抽离单一职责的模块几乎是必须的:若需维护多个项目,抽离公用包上传私有仓库管理也几乎是必须的.其优点无需赘述,以下将记录操作过程. 1. 多模块拆分 在.NET 中由于其统一性,实现上 ...

  7. Spring Boot从零入门2_核心模块详述和开发环境搭建

    目录 1 前言 2 名词术语 3 Spring Boot核心模块 3.1 spring-boot(主模块) 3.2 spring-boot-starters(起步依赖) 3.3 spring-boot ...

  8. Spring Boot

    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过 ...

  9. 深入学习微框架:Spring Boot(转)

    转:http://www.infoq.com/cn/articles/microframeworks1-spring-boot/ 相关参考: https://spring.io/guides/gs/s ...

随机推荐

  1. JavaAppArguments示例

    本实验要求编写一个程序,此程序从命令行接收多个数字,求和之后输出结果.一大难点是命令行参数都是字符串,必须先将其转化为数字,才能相加. 中心想法就是将求和数字转换为整型并依次相加. 程序流程图: pu ...

  2. CentOS 6.7中安装python3.5

    1.安装一些依赖的软件包 yum groupinstall "Development tools" yum install zlib-devel bzip2-devel opens ...

  3. 692. Top K Frequent Words

    Given a non-empty list of words, return the k most frequent elements. Your answer should be sorted b ...

  4. JavaScript(3)——Object-Oriented Design

    自己定义函数 var Winston = function(nickname, age, x, y) { this.nickname = nickname; this.age = age + &quo ...

  5. 浅谈《守望先锋》中的 ECS 构架

    https://blog.codingnow.com/2017/06/overwatch_ecs.html 今天读了一篇 <守望先锋>架构设计与网络同步 .这是根据 GDC 2017 上的 ...

  6. Java性能优化之高性能JAVA代码的若干个习惯

    创建对象: 1.避免在循环体中创建对象,循环前应该创建对象,避免浪费更多内存空间和增加GC负担 这种情况在我们的实际应用中经常遇到,而且我们很容易犯类似的错误,例如下面的代码: for (int i ...

  7. 【转】ROWNUM与ORDER BY先后关系

    源地址:http://www.cnblogs.com/accumulater/p/6137385.html

  8. C# 服务器端控件

    服务器端控件和客户端控件的比较? 区别:服务器端控件都会有个runat="Server"属性,这样才能够在后台对其进行设置修改,也就是在cs代码里面能对其修改设置.你做下测试 你放 ...

  9. ubuntu部署django详细教程

    教程使用的软件版本:Ubuntu 18.04.1 LTS,django2.0,Python 3.6.5.nginx-1.13.7.uWSGI (2.0.17.1),Ubuntu是纯净的,全新的.下面我 ...

  10. [USACO08NOV]安慰奶牛Cheering up the Cow BZOJ 1232 Kruskal

    Farmer John变得非常懒, 他不想再继续维护供奶牛之间供通行的道路. 道路被用来连接N (5 <= N <= 10,000)个牧场, 牧场被连续地编号为1..N. 每一个牧场都是一 ...