第19章 用 Spring Boot 和 MongoDB GridFS实现大文件存储与管理

本文通过一个在线教育平台的实际案例,详细介绍如何使用 Spring Boot 和 MongoDB 的 GridFS 功能来存储和管理大文件(如视频、图片等)。我们将从环境搭建、代码实现到测试验证,一步步带你掌握 GridFS 的核心用法,让你轻松应对大文件存储的需求。

知识回顾

简介

MongoDB 是一种文档型数据库,主要用于存储 JSON 格式的数据,但它也能处理普通的文件。通过 BSON 格式,MongoDB 可以轻松保存各种类型的文件,比如图片或文本文件。不过,单个 BSON 文档的大小不能超过 16MB,这限制了大文件的直接存储。为了解决这个问题,MongoDB 提供了 GridFS 功能,允许我们存储大于 16MB 的文件。特点注意:GridFS不支持多文档事务。

基本原理

GridFS 的工作方式很简单:将大文件分割成多个小块(称为 chunks),然后分别存储这些小块。每个文件的相关信息会被保存在一个名为 fs.files 的集合中,而实际的文件内容则会被分割并存储在另一个名为 fs.chunks 的集合里。

  1. 文件信息 (fs.files) 每条记录包含了文件的基本信息,如:
  • _id: 文件的唯一标识符。
  • length: 文件的总大小。
  • chunkSize: 每个 chunk 的大小,决定了文件会被分成多少部分。
  • uploadDate: 文件上传的时间。
  • filename: 文件的名字。
  • metadata: 用户可以自定义的额外信息,便于后续的查找和使用。
  1. 文件内容 (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 功能来存储和检索大文件(例如视频或大型图片文件)。

任务准备

  1. 安装 MongoDB 数据库。
  2. 安装 MongoDB Compass 或Navicat Premium 16其他图形化管理工具,用于查看数据库状态。
  3. 准备好一个大文件(例如一个 50MB 的视频文件)作为测试用例。
  4. 安装和配置好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测试上传下载测试

上传文件接口测试:

  1. 打开 Postman

  2. 选择 POST 请求

  3. 在 URL 中输入 http://localhost:81/files/upload(假设你的 Spring Boot 应用运行在本地 81 端口)。

  4. 在 Body 选项中选择 form-data

  5. 添加一个键值对

    • Key: file
    • Value: 选择一个文件:test.mp4
  6. 发送请求,检查响应是否返回文件 ID。

下载文件接口测试:

  1. 选择 GET 请求
  2. 在 URL 中输入 http://localhost:81/files/download/{fileId},将 {fileId} 替换为在上传文件时获得的文件 ID。
  3. 发送请求,检查响应是否返回文件内容并提示下载。

实验实训

  • 尝试上传不同类型的文件(如图片、PDF、视频等)。
  • 修改 chunkSize 参数,观察对上传时间和存储效率的影响。
  • 实现一个简单的 Web 接口,允许用户上传和下载文件。
  • 探索 GridFS 的元数据功能,给文件添加标签或其他信息,以便于分类和搜索。
相关推荐
小周不摆烂1 小时前
Java基础-JDBC
java·数据库·oracle
是桃萌萌鸭~2 小时前
导出 MySQL 中所有表的结构(包括外键约束),并在另一个地方创建相同的表
数据库·mysql
Fisher36522 小时前
SQL 常用更新操作
数据库·sql
阿葱(聪)2 小时前
java.lang.NoClassDefFoundError: kotlin/jvm/JvmInline
数据库·oracle
Mr.王8352 小时前
架构学习第四周--高可用与NoSQL数据库
linux·nosql
KELLENSHAW2 小时前
MySQL45讲 第十六讲 “order by”是怎么工作的?
数据库·mysql
hummhumm2 小时前
Oracle 第20章:数据库调优
java·数据库·后端·python·mysql·oracle·database
JingHongB3 小时前
Redis的常用数据类型以及命令
数据库·redis·缓存
ffyyhh9955113 小时前
SpringBoot事务管理:自调用与事务回滚行为分析
数据库
小光学长3 小时前
基于vue框架的的楼盘销售管理系统6n60a(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
数据库