MongoDB GridFS 存储文件
前言
在开发中,会碰到存储图片或者视频的问题,也就是大文件存储的问题,能想到的有三种解决方式:
- 本地文件系统
- 数据库,比如 MongoDB
- 云存储,比如 Amazon S3
其他两种都比较好理解,现在想来看一下 MongoDB 是怎么来存储图片或者视频的。看到官网的文档(版本是 5.0)是如果文件大小 <
16MB,一个文档是可以存的下的,超过 16MB 的话需要用 GridFS
。
关于 GridFS
GridFS
是 MongoDB 中存储超过 16M 文件的存储方式和约定,实现的思路就是分而治之。GridFS 默认的块(chunk)大小为 256kB
,用两个 collection(chunks
) 去存文件,一个 collection(files
) 来存储切分的 chunk,另外一个 collection 来存储文件的元数据。这两个 collection 都在同一个 bucket 名为 fs
下。
什么时候用 GridFS
在 MongoDB 中,虽然使用 GridFS 来存储超过 16MB
大小的文件,但是有些情况下,用 MongoDB 来存储文可能比本地文件系统来存储有效的多。官方文档给了下面三种情况:
- 本地文件系统有文件或者目录的限制,GridFS 没有限制
- 不需要拿整个文件加载到内存就可以取到文件的所需要的部分数据,GridFS 可以做到。
- 文件自动同步,尤其在分布式的应用上。
怎么使用 GridFS
基于 spring boot 搭建的示例项目来展示一下:
配置
1
2
3
4
5
6
7
8
9# 配置上传单个文件大小无限制
-1 =
# 配置上传总大小无限制
-1 =
# MongoDB 的配置
mongodb://root:root123@127.0.0.1:27017 =
test =
uploads =thymeleaf 模板页面代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42<html xmlns:th="https://www.thymeleaf.org">
<body>
<h1><font color="blue">上传文件到 MongoDB </font> </h1>
<div th:if="${message}">
<h2 th:text="${message}"/>
</div>
<div>
<form method="POST" enctype="multipart/form-data" action="/gridFs/upload">
<table>
<tr>
<td>File to upload:</td>
<td><input type="file" name="file"/></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Upload"/></td>
</tr>
</table>
</form>
<form method="POST" action="/gridFs/delete">
<table>
<tr>
<td></td>
<td><input type="submit" value="deleteAll"/></td>
</tr>
</table>
</div>
<div>
<ul>
<li th:each="file : ${files}">
<a th:href="@{'http://127.0.0.1:8080/gridFs/' + ${file} + '/download'}" th:text="${file}"/>
</li>
</ul>
</div>
</body>
</html>Controller
代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class GridFsController {
private final GridFsService gridFsService;
public GridFsController(GridFsService gridFsService) {
this.gridFsService = gridFsService;
}
// 主页
public String listUploadFiles(Model model) {
model.addAttribute("files", gridFsService.loadAllFiles());
return "gridFsForm";
}
// 上传接口
public String handleFileUpload( MultipartFile file, RedirectAttributes redirectAttributes){
final long start = System.currentTimeMillis();
gridFsService.upload(file, "user" + new Random().nextInt(100));
final long end = System.currentTimeMillis();
final long period = end - start;
final long t = period / 1000;
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded " + file.getOriginalFilename() + "!" +
" spent time : " + ( t < 1 ? period + "ms" : t + "s"));
return "redirect:/gridFs/";
}
// 下载接口
public ResponseEntity<Resource> serverFile( String filename){
Resource file = gridFsService.download(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(file.getFilename(),
StandardCharsets.UTF_8) + "\"")
.body(file);
}
// 删除所有的文件接口
public String deleteAllFiles(RedirectAttributes redirectAttributes) {
gridFsService.deleteAll();
redirectAttributes.addFlashAttribute("message",
"You successfully deleted all files !");
return "redirect:/gridFs/";
}
}Service
代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class GridFsServiceImpl implements GridFsService {
GridFsTemplate gridFsTemplate;
public void upload(MultipartFile file, String name) {
final String fileName = file.getName();
System.out.println(fileName);
final BasicDBObject basicDBObject = new BasicDBObject();
basicDBObject.put("user", name);
ObjectId objectId = null;
try {
objectId = gridFsTemplate.store(file.getInputStream(),
file.getOriginalFilename(),
file.getContentType(),
basicDBObject);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("object id:" + objectId.toString());
}
public List<String> loadAllFiles() {
List<GridFSFile> gridFSFiles = new ArrayList<>();
gridFsTemplate.find(new Query()).into(gridFSFiles);
final List<String> files = gridFSFiles.stream().map(GridFSFile::getFilename).collect(Collectors.toList());
return files;
}
public Resource download(String fileName) {
final GridFsResource[] resources = gridFsTemplate.getResources(fileName + "*");
return resources[0];
}
public void deleteAll() {
gridFsTemplate.delete(new Query());
}
}演示
MongoDB GridFS 存储文件