文章目录
- 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的原因:
- 存储巨大的文件,如视频、图片等。
- 利用GridFS简化需求。
- GridFS会直接利用已经建立的复制或分片机制,故障恢复和扩展容易。
- GridFS可以避免用户上传内容的文件系统出现问题。
- GridFS不产生磁盘碎片。
GridFS使用俩个表来存储数据:
- files:包含元数据对象(如文件的名称、上传的时间)
- 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()
@Testpublic 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 {@AutowiredGridFsOperations 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";}}
测试结果: