文章目录

  • GridFS
  • GridFS简介
  • GridFS存储原理
  • GridFS整合SpringBoot
  • 新增store()
  • 查询与下载find()、findOne()
  • 删除delete()
  • Demo案例


GridFS

GridFS简介

GridFS是MongoDB的一个用来存储/获取大型数据(图像、音频、视频等类型的文件)的规范。相当于一个存储文件的文件系统,但它的数据存储在MongoDB的集合中。GridFS能存储超过文档大小(16MB)限制的文件。
GridFS将文件分解成块,将每块数据保存在不同的文档中,每块大小最大为255KB。

GridFS存储原理

GridFS使用两个集合(collection)存储文件。一个集合是chunks,用于存储文件内容的二进制数据,一个集合是files,用于存储文件的元数据。
GridFS是一种在MongoDB中存储大二进制文件的机制,使用GridFS的原因:

  1. 存储巨大的文件,如视频、图片等。
  2. 利用GridFS简化需求。
  3. GridFS会直接利用已经建立的复制或分片机制,故障恢复和扩展容易。
  4. GridFS可以避免用户上传内容的文件系统出现问题。
  5. GridFS不产生磁盘碎片。

GridFS使用俩个表来存储数据:

  1. files:包含元数据对象(如文件的名称、上传的时间)
  2. chunks:包含其他一些相关信息的二进制块

    fs.files集合存储文件的元数据,以类的json文档格式存储。每在GridFS存储一个文件,则会在fs.files集合中生成一个文档。
    fs.filse集合中文档的存储内容如下:
{
	"_id": <ObjectId>,		//(必填)文档ID,唯一标识
	"chunkSize": <num>,		//(必填)chunk大小 256kb
	"uploadDate": <timestamp>,	//(必填)文件第一次上传时间
	"length": <num>,	//(必填)文件长度
	"md5": <string>,	//(必填)文件md5的值
	"filename": <string>,		//(可选)文件名
	"contentType":<string> ,	//(可选)文件的MIME类型
	"metadata": <dataObject>		//(可选)文件自定义的信息
}

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

{
	"_id": <ObjectId>,	// (必填)文档唯一表示
	"files_id": <ObjectId>,	//(必填)对应fs.files文档的ID
	"n": <num>,	// (必填)序号,表示文件的第几个chunk
	"data": <binary>	// (必填)文件二进制数据
}

GridFS整合SpringBoot

新增store()

@Autowired
private GridFsTemplate gridFsTemplate;
@Autowired
private GridFSBucket gridFSBucket;
@Test
public void test_insert() throws FileNotFoundException {
    File file = new File("G:\\test.txt");
    ObjectId objId = gridFsTemplate.store(new FileInputStream(file), "测试.txt", "txt");
    System.out.println(objId.toString());
}

查询与下载find()、findOne()

@Test
public void test_read() throws IOException {
    System.out.println("============ 查询所有文件 ==============");
    GridFSFindIterable files = gridFsTemplate.find(new Query());
    files.forEach(f -> {
        System.out.println(f.toString());
    });
    System.out.println("============ 根据ID查找文件 ==============");
    String fileId = "63b97c1c9b7cf822eeac502a";
    GridFSFile file = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
    System.out.println(file.toString());
    System.out.println("============ 查找到对应的文件,读取文件 ==============");
    GridFSDownloadStream stream = gridFSBucket.openDownloadStream(file.getObjectId());
    GridFsResource gridFsResource = new GridFsResource(file, stream);
    InputStream inputStream = gridFsResource.getInputStream();
    this.fileOutPut(inputStream, gridFsResource.getFilename());
}
private void fileOutPut(InputStream inputStream, String filename) throws IOException {
    File f1 = new File("G:\\test\\" + filename);
    if (f1.exists()) f1.getParentFile().mkdir();
    FileOutputStream fos = new FileOutputStream(f1);
    byte[] bytes = new byte[1024];
    int len = 0;
    while ((len = inputStream.read(bytes)) != -1) {
        fos.write(bytes, 0, len);
    }
    inputStream.close();
    fos.close();
}

删除delete()

@Test
 public void test_delete(){
     String fileId = "63b97c1c9b7cf822eeac502a";
     gridFsTemplate.delete(Query.query(Criteria.where("_id").is(fileId)));
 }

Demo案例

package com.hx.demo1.controller;


import com.mongodb.client.gridfs.GridFSFindIterable;
import com.mongodb.client.gridfs.model.GridFSFile;
import org.bson.BsonValue;
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.GridFsOperations;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Huathy
 * @date 2023-01-08 00:19
 * @description
 */
@RestController
@RequestMapping(value = "file", method = {RequestMethod.GET, RequestMethod.POST})
public class FileController {
    @Autowired
    GridFsOperations gridFsOperations;

    /**
     * 上传文件
     * @param file
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Map<String, ObjectId> insert(@RequestPart(value = "file") MultipartFile file) throws IOException {
        //开始时间
        long begin = System.nanoTime();
        Map<String, ObjectId> map = new HashMap<>();
        InputStream streamForUpload = file.getInputStream();
        ObjectId objectId = gridFsOperations.store(streamForUpload, file.getOriginalFilename(), file.getContentType(), "测试上传");
        //上传结束
        long time = System.nanoTime() - begin;
        System.out.println("本次上传共耗时: " + time);
        System.out.println("上传成功!文件名: " + file.getOriginalFilename() + ". 文件ID: " + objectId);
        map.put(file.getOriginalFilename(), objectId);
        return map;
    }

    /**
     * 获取所有文件的ID
     * @return
     */
    @RequestMapping("/allFiles")
    public ArrayList<Object> findAllFiles() {
        GridFSFindIterable gridFSFiles = gridFsOperations.find(new Query());
        ArrayList<Object> ids = new ArrayList<>();
        gridFSFiles.forEach(f -> {
            BsonValue bsonValue = f.getId();
            ids.add(bsonValue.toString());
        });
        return ids;
    }

    /**
     * 根据ID下载文件
     * @param id        mongoDB的文件ID
     * @param response
     * @return
     * @throws IOException
     */
    @RequestMapping("/download")
    public String download(String id, HttpServletResponse response) throws IOException {
        GridFSFile file = gridFsOperations.findOne(Query.query(Criteria.where("_id").is(id)));
        if (file != null) {
            GridFsResource resource = gridFsOperations.getResource(file);
            response.reset();
            response.setContentType(resource.getContentType());
            response.setHeader("Content-Disposition", "attachment;filename=" + resource.getFilename());
            ServletOutputStream ops = response.getOutputStream();
            InputStream ips = resource.getInputStream();
            int len = 0;
            byte[] bytes = new byte[1024];
            while ((len = ips.read(bytes)) != -1) {
                ops.write(bytes, 0, len);
            }
            ips.close();
            ops.close();
            return resource.getContentType();
        }
        throw new RuntimeException("没有找到相关文件");
    }

    /**
     * 删除文件
     * @param id    mongodb的文件ID
     * @return
     */
    @GetMapping("/delete")
    public String deleteFile(String id){
        gridFsOperations.delete(Query.query(Criteria.where("_id").is(id)));
        return "delete success";
    }

}

测试结果:

mongodb存储小文件 mongodb大文件存储规范的原理_System

mongodb存储小文件 mongodb大文件存储规范的原理_spring boot_02