本文通过一个在线教育平台的实际案例,详细介绍如何使用 Spring Boot 和 MongoDB 的 GridFS 功能来存储和管理大文件(如视频、图片等)。我们将从环境搭建、代码实现到测试验证,一步步带你掌握 GridFS 的核心用法,让你轻松应对大文件存储的需求。
知识回顾
简介
MongoDB 是一种文档型数据库,主要用于存储 JSON 格式的数据,但它也能处理普通的文件。通过 BSON 格式,MongoDB 可以轻松保存各种类型的文件,比如图片或文本文件。不过,单个 BSON 文档的大小不能超过 16MB,这限制了大文件的直接存储。为了解决这个问题,MongoDB 提供了 GridFS 功能,允许我们存储大于 16MB 的文件。特点注意:GridFS不支持多文档事务。
基本原理
GridFS 的工作方式很简单:将大文件分割成多个小块(称为 chunks),然后分别存储这些小块。每个文件的相关信息会被保存在一个名为 fs.files 的集合中,而实际的文件内容则会被分割并存储在另一个名为 fs.chunks 的集合里。
- 文件信息 (fs.files) 每条记录包含了文件的基本信息,如:
- _id: 文件的唯一标识符。
- length: 文件的总大小。
- chunkSize: 每个 chunk 的大小,决定了文件会被分成多少部分。
- uploadDate: 文件上传的时间。
- filename: 文件的名字。
- metadata: 用户可以自定义的额外信息,便于后续的查找和使用。
- 文件内容 (fs.chunks) 每个 chunk 由一条记录表示,包含的信息有:
- _id: chunk 的唯一标识符。
- files_id: 该 chunk 所属文件的 ID。
- n: chunk 的序号,从 0 开始计数。
- data: 实际的文件内容。
当文件大小不超过 chunkSize 时,fs.files 和 fs.chunks 中都只会有一条记录。但如果文件较大,则 fs.files 中仍只有一个条目,而 fs.chunks 中会有多个条目来存储不同的 chunk。
MongoDB 还会自动创建索引来加快查询速度,例如对 fs.files 集合中的文件名和上传时间建立索引,以及对 fs.chunks 集合中的文件 ID 和 chunk 序号建立索引。
通过这种方式,GridFS 让我们在 MongoDB 中高效地管理和访问大文件成为了可能。
任务描述
假设您正在开发一个在线教育平台,需要存储大量的教学视频资源。由于视频文件通常比较大,直接使用 BSON 格式存储可能会遇到 16MB 的大小限制。因此,您决定采用 GridFS 来解决这个问题。学习如何使用 MongoDB 的 GridFS 功能来存储和检索大文件(例如视频或大型图片文件)。
任务准备
- 安装 MongoDB 数据库。
- 安装 MongoDB Compass 或Navicat Premium 16其他图形化管理工具,用于查看数据库状态。
- 准备好一个大文件(例如一个 50MB 的视频文件)作为测试用例。
- 安装和配置好postman测试工具
任务实施
源代码地址:study-springboot-chapter22 gitee.com/ossbar/stud...
步骤 1:创建 Spring Boot 项目
- 使用 Spring Initializr 创建一个新的 Spring Boot 项目。
- 添加 Spring Web, Spring Data MongoDB 和 Thymeleaf 依赖。
步骤 2:依赖配置:
在 pom.xml 文件中添加必要的依赖项:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
在application.yml文件中添加mongodb相关配置
yaml
spring:
data:
mongodb:
uri: mongodb://localhost:27019
database: student
步骤3:创建MongoConfig注入GridFsTemplate
kotlin
package com.cbitedu.springboot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.data.mongodb.core.MongoTemplate;
import com.mongodb.client.MongoClient;
@Configuration
public class MongoConfig {
@Bean
public GridFsTemplate gridFsTemplate(MongoTemplate mongoTemplate) {
return new GridFsTemplate(mongoTemplate.getMongoDbFactory(), mongoTemplate.getConverter());
}
}
步骤4:创建FileService文件服务类
kotlin
package com.cbitedu.springboot.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@Service
public class FileService {
@Autowired
private GridFsTemplate gridFsTemplate;
// 上传文件
public String uploadFile(MultipartFile file) throws IOException {
return gridFsTemplate.store(file.getInputStream(), file.getOriginalFilename(), file.getContentType()).toString();
}
// 下载文件
public GridFsResource downloadFile(String id) {
return gridFsTemplate.getResource(id);
}
}
步骤5:创建FileController文件控制类
kotlin
package com.cbitedu.springboot.controller;
import com.cbitedu.springboot.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@RestController
@RequestMapping("/files")
public class FileController {
@Autowired
private FileService fileService;
// 上传文件接口
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
String fileId = fileService.uploadFile(file);
return ResponseEntity.ok(fileId);
}
// 下载文件接口
@GetMapping("/download/{id}")
public ResponseEntity<?> downloadFile(@PathVariable String id) {
try {
GridFsResource resource = fileService.downloadFile(id);
if (resource == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("文件不存在");
}
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="" + resource.getFilename() + """)
.body(new InputStreamResource(resource.getInputStream()));
} catch (IOException e) {
// 处理下载文件时的异常
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件下载失败:" + e.getMessage());
}
}
}
步骤5:postman测试上传下载测试
上传文件接口测试:
-
打开 Postman。
-
选择 POST 请求。
-
在 URL 中输入
http://localhost:81/files/upload
(假设你的 Spring Boot 应用运行在本地 81 端口)。 -
在 Body 选项中选择 form-data。
-
添加一个键值对:
- Key:
file
- Value: 选择一个文件:test.mp4
- Key:
-
发送请求,检查响应是否返回文件 ID。
下载文件接口测试:
- 选择 GET 请求。
- 在 URL 中输入
http://localhost:81/files/download/{fileId}
,将{fileId}
替换为在上传文件时获得的文件 ID。 - 发送请求,检查响应是否返回文件内容并提示下载。
实验实训
- 尝试上传不同类型的文件(如图片、PDF、视频等)。
- 修改 chunkSize 参数,观察对上传时间和存储效率的影响。
- 实现一个简单的 Web 接口,允许用户上传和下载文件。
- 探索 GridFS 的元数据功能,给文件添加标签或其他信息,以便于分类和搜索。