文章目录
- 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()
@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";
}
}
测试结果: