zoukankan      html  css  js  c++  java
  • Spring Boot使用mongo的GridFS模块

    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的启动函数

      1 package com.ws;
      2 
      3 import org.springframework.boot.SpringApplication;
      4 import org.springframework.boot.autoconfigure.SpringBootApplication;
      5 
      6 @SpringBootApplication
      7 public class Application {
      8     public static void main(String[] args) {
      9         SpringApplication.run(Application.class, args);
     10     }
     11 }
    View Code

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

      1 package com.ws;
      2 
      3 public class Response {
      4     private String name;
      5 
      6     public Response(String name) {
      7         this.name = name;
      8     }
      9 
     10     public String getName() {
     11         return name;
     12     }
     13 
     14     public void setName(String name) {
     15         this.name = name;
     16     }
     17 
     18 }
    View Code

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

      1 package com.ws;
      2 
      3 import com.mongodb.BasicDBObject;
      4 import com.mongodb.DBObject;
      5 import com.mongodb.gridfs.GridFSDBFile;
      6 import org.apache.commons.io.IOUtils;
      7 import org.apache.log4j.Logger;
      8 import org.springframework.beans.factory.annotation.Autowired;
      9 import org.springframework.data.mongodb.core.query.Criteria;
     10 import org.springframework.data.mongodb.core.query.Query;
     11 import org.springframework.data.mongodb.gridfs.GridFsTemplate;
     12 import org.springframework.http.MediaType;
     13 import org.springframework.web.bind.annotation.RequestMapping;
     14 import org.springframework.web.bind.annotation.RequestMethod;
     15 import org.springframework.web.bind.annotation.RequestParam;
     16 import org.springframework.web.bind.annotation.RestController;
     17 import org.springframework.web.multipart.MultipartFile;
     18 
     19 import java.io.IOException;
     20 import java.io.InputStream;
     21 import java.util.Date;
     22 import java.util.List;
     23 import java.util.UUID;
     24 
     25 @RestController
     26 @RequestMapping("/api")
     27 public class GridFSApi {
     28     private static Logger LOGGER = Logger.getLogger(GridFSApi.class);
     29     @Autowired
     30     private GridFsTemplate gridFsTemplate;
     31 
     32     @RequestMapping(value = "/save", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
     33     public Response save(@RequestParam(value = "file", required = true) MultipartFile file) {
     34 
     35         LOGGER.info("Saving file..");
     36         DBObject metaData = new BasicDBObject();
     37         metaData.put("createdDate", new Date());
     38 
     39         String fileName = UUID.randomUUID().toString();
     40 
     41         LOGGER.info("File Name: " + fileName);
     42 
     43         InputStream inputStream = null;
     44         try {
     45             inputStream = file.getInputStream();
     46             gridFsTemplate.store(inputStream, fileName, "image", metaData);
     47             LOGGER.info("File saved: " + fileName);
     48         } catch (IOException e) {
     49             LOGGER.error("IOException: " + e);
     50             throw new RuntimeException("System Exception while handling request");
     51         }
     52         LOGGER.info("File return: " + fileName);
     53         return new Response(fileName);
     54     }
     55 
     56     @RequestMapping(value = "/get", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
     57     public byte[] get(@RequestParam(value = "fileName", required = true) String fileName) throws IOException {
     58         LOGGER.info("Getting file.." + fileName);
     59         List<GridFSDBFile> result = gridFsTemplate
     60                 .find(new Query().addCriteria(Criteria.where("filename").is(fileName)));
     61         if (result == null || result.size() == 0) {
     62             LOGGER.info("File not found" + fileName);
     63             throw new RuntimeException("No file with name: " + fileName);
     64         }
     65         LOGGER.info("File found " + fileName);
     66         return IOUtils.toByteArray(result.get(0).getInputStream());
     67     }
     68 
     69     @RequestMapping(value = "/delete", method = RequestMethod.DELETE)
     70     public void delete(@RequestParam(value = "fileName", required = true) String fileName) {
     71         LOGGER.info("Deleting file.." + fileName);
     72         gridFsTemplate.delete(new Query().addCriteria(Criteria.where("filename").is(fileName)));
     73         LOGGER.info("File deleted " + fileName);
     74     }
     75 }
     76 
    View Code
  • 相关阅读:
    DS4700磁盘阵列的控制器微码升级操作记录(收录百度文库)
    Android 解决布局无法对齐的情况
    android 模仿大众点评团购卷列表多余3条时折叠,点击时显示剩余全部的功能
    android 解决ScrollView中的子布局不能够填充整个ScrollView的情况。
    Android在代码中设置控件的drawableLeft,drawableRight,drawableTop,drawableBottom。
    android RadioGroup中设置selector后出现多个别选中的RadioButton的解决办法
    Android 动态的给Button、TextView、ImageView等控件设置了background后,再设置padding属性时该属性不起作用
    Android Universal Image Loader java.io.FileNotFoundException: http:/xxx/lxx/xxxx.jpg
    Android2.3系统 自定义的PopupWindow在实例化时报空指针异常
    android精品开源项目整理
  • 原文地址:https://www.cnblogs.com/mengrennwpu/p/8849551.html
Copyright © 2011-2022 走看看