1. 概述

在本教程中,我们将探讨如何使用 MongoDB 和 Spring Boot 实现文件的上传与获取。

📌 我们将使用 MongoDB 的 BSON 存储小文件(小于 16MB),使用 GridFS 存储大文件。

2. Maven 配置

首先,在 pom.xml 中添加 Spring Boot MongoDB 的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

此外,为了实现 Web 页面交互,还需要加入:

  • spring-boot-starter-web(用于构建 Web 应用)
  • spring-boot-starter-thymeleaf(用于前端模板渲染)

这些依赖在我们之前的文章《Spring Boot 与 Thymeleaf 入门指南》中有详细说明。

✅ 本文使用 Spring Boot 2.x 系列版本。

3. Spring Boot 属性配置

接着配置必要的 Spring Boot 属性。

3.1 MongoDB 配置

spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=springboot-mongo

3.2 文件上传配置

spring.servlet.multipart.max-file-size=256MB
spring.servlet.multipart.max-request-size=256MB
spring.servlet.multipart.enabled=true

⚠️ 注意:Spring Boot 2.x 中使用 spring.servlet 而非 spring.http,确保配置路径正确。

4. 小文件上传

我们使用 MongoDB 的 BSON Binary 类型来存储小文件(<16MB)。

4.1 定义 Photo 实体类

@Document(collection = "photos")
public class Photo {
    @Id
    private String id;
    private String title;
    private Binary image;
}

4.2 定义 Repository

public interface PhotoRepository extends MongoRepository<Photo, String> { }

4.3 实现 PhotoService

@Service
public class PhotoService {

    @Autowired
    private PhotoRepository photoRepo;

    public String addPhoto(String title, MultipartFile file) throws IOException {
        Photo photo = new Photo(title);
        photo.setImage(new Binary(BsonBinarySubType.BINARY, file.getBytes()));
        photo = photoRepo.insert(photo);
        return photo.getId();
    }

    public Photo getPhoto(String id) {
        return photoRepo.findById(id).get();
    }
}

5. 大文件上传

对于大于 16MB 的文件,我们使用 MongoDB 的 GridFS 进行存储。

5.1 定义 Video DTO

public class Video {
    private String title;
    private InputStream stream;
}

5.2 实现 VideoService

@Service
public class VideoService {

    @Autowired
    private GridFsTemplate gridFsTemplate;

    @Autowired
    private GridFsOperations operations;

    public String addVideo(String title, MultipartFile file) throws IOException {
        DBObject metaData = new BasicDBObject();
        metaData.put("type", "video");
        metaData.put("title", title);
        ObjectId id = gridFsTemplate.store(
            file.getInputStream(), file.getName(), file.getContentType(), metaData);
        return id.toString();
    }

    public Video getVideo(String id) throws IllegalStateException, IOException {
        GridFSFile file = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(id)));
        Video video = new Video();
        video.setTitle(file.getMetadata().get("title").toString());
        video.setStream(operations.getResource(file).getInputStream());
        return video;
    }
}

✅ 提示:关于 GridFS 的更多使用细节,可参考我们之前的文章《在 Spring Data MongoDB 中使用 GridFS

6. 控制器实现

6.1 PhotoController

@PostMapping("/photos/add")
public String addPhoto(@RequestParam("title") String title, 
                       @RequestParam("image") MultipartFile image, Model model) throws IOException {
    String id = photoService.addPhoto(title, image);
    return "redirect:/photos/" + id;
}

@GetMapping("/photos/{id}")
public String getPhoto(@PathVariable String id, Model model) {
    Photo photo = photoService.getPhoto(id);
    model.addAttribute("title", photo.getTitle());
    model.addAttribute("image", Base64.getEncoder().encodeToString(photo.getImage().getData()));
    return "photos";
}

✅ 注意:我们使用 Base64 编码将图片数据传给前端渲染。

6.2 VideoController

@PostMapping("/videos/add")
public String addVideo(@RequestParam("title") String title, 
                       @RequestParam("file") MultipartFile file, Model model) throws IOException {
    String id = videoService.addVideo(title, file);
    return "redirect:/videos/" + id;
}

@GetMapping("/videos/{id}")
public String getVideo(@PathVariable String id, Model model) throws Exception {
    Video video = videoService.getVideo(id);
    model.addAttribute("title", video.getTitle());
    model.addAttribute("url", "/videos/stream/" + id);
    return "videos";
}

@GetMapping("/videos/stream/{id}")
public void streamVideo(@PathVariable String id, HttpServletResponse response) throws Exception {
    Video video = videoService.getVideo(id);
    FileCopyUtils.copy(video.getStream(), response.getOutputStream());
}

7. 前端页面实现

7.1 uploadPhoto.html

<html>
<body>
<h1>上传新图片</h1>
<form method="POST" action="/photos/add" enctype="multipart/form-data">
    标题:<input type="text" name="title" />
    图片:<input type="file" name="image" accept="image/*" />
    <input type="submit" value="上传" />
</form>
</body>
</html>

7.2 photos.html

<html>
<body>
    <h1>查看图片</h1>
    标题:<span th:text="${title}">name</span>
    <img alt="sample" th:src="*{'data:image/png;base64,'+image}" />
</body>
</html>

7.3 uploadVideo.html

<html>
<body>
<h1>上传新视频</h1>
<form method="POST" action="/videos/add" enctype="multipart/form-data">
    标题:<input type="text" name="title" />
    视频:<input type="file" name="file" accept="video/*" />
    <input type="submit" value="上传" />
</form>
</body>
</html>

7.4 videos.html

<html>
<body>
    <h1>查看视频</h1>
    标题:<span th:text="${title}">title</span>
    <video width="400" controls>
        <source th:src="${url}" />
    </video>
</body>
</html>

8. 总结

在本教程中,我们学习了如何使用 MongoDB 和 Spring Boot 实现文件上传与获取:

  • 小文件(<16MB)使用 BSON Binary 存储
  • 大文件使用 GridFS 存储
  • 使用 Thymeleaf 构建简单前端页面
  • 使用 Base64 编码渲染图片
  • 使用流式传输播放视频

完整源码可访问 GitHub 示例项目

✅ 踩坑提醒:Spring Boot 2.x 中文件上传配置使用 spring.servlet.multipart,而非旧版本的 spring.http.multipart,注意版本差异。


原始标题:Upload and Retrieve Files Using MongoDB and Spring Boot