1. Overview

In this tutorial, we’ll discuss how to upload and retrieve files using MongoDB and Spring Boot.

We’ll use MongoDB BSON for small files and GridFS for the larger ones.

2. Maven Configuration

First, we’ll add the spring-boot-starter-data-mongodb dependency to our pom.xml:

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

In addition, we’ll need the spring-boot-starter-web and spring-boot-starter-thymeleaf dependencies to display the user interface of our application. These dependencies are also shown in our Guide to Spring Boot with Thymeleaf.

In this tutorial, we’re using Spring Boot version 2.x.

3. Spring Boot Properties

Next, we’ll configure the necessary Spring Boot properties.

Let’s start with the MongoDB properties:

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

We’ll also set the Servlet Multipart properties to allow uploading large files:

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

4. Uploading Small Files

Now, we’ll discuss how to upload and retrieve small files (size < 16MB) using MongoDB BSON.

Here, we have a simple Document class — Photo. We’ll store our image file in a BSON Binary:

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

And we’ll have a simple PhotoRepository:

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

Now, for the PhotoService, we’ll have only two methods:

  • addPhoto() — to upload a Photo to MongoDB
  • getPhoto() — to retrieve a Photo with a given id
@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. Uploading Large Files

Now, we’ll use GridFS to upload and retrieve large files.

First, we’ll define a simple DTO – Video – to represent a large file:

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

Similar to the PhotoService, we’ll have a VideoService with two methods — addVideo() and getVideo():

@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; 
    }
}

For more details on using GridFS with Spring, check our GridFS in Spring Data MongoDB article.

6. Controllers

Now, let’s take a look at the controllers — PhotoController and VideoController.

6.1. PhotoController

First, we have the PhotoController, which will use our PhotoService to add/get photos.

We’ll define the addPhoto() method to upload and create a new Photo:

@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;
}

We also have getPhoto() to retrieve a Photo with a given 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";
}

*Note that as we have the image data returned as a byte[], we’ll convert it to a Base64 String to display it on the front-end.*

6.2. VideoController

Next, let’s have a look at our VideoController.

This will have a similar method, addVideo(), to upload a Video to our MongoDB:

@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;
}

And here we have getVideo() to retrieve a Video with a given 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";
}

We can also add a streamVideo() method that will create a streaming URL from the Video InputStream:

@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. Front-End

Finally, let’s see our front-end.
Let’s start with uploadPhoto.html, which provides a simple form to upload an image:

<html>
<body>
<h1>Upload new Photo</h1>
<form method="POST" action="/photos/add" enctype="multipart/form-data">
    Title:<input type="text" name="title" />
    Image:<input type="file" name="image" accept="image/*" />
    <input type="submit" value="Upload" />
</form>
</body>
</html>

Next, we’ll add the photos.html view to display our photos:

<html>
<body>
    <h1>View Photo</h1>
    Title: <span th:text="${title}">name</span>
    <img alt="sample" th:src="*{'data:image/png;base64,'+image}" />
</body>
</html>

Similarly, we have the uploadVideo.html to upload a Video:

<html>
<body>
<h1>Upload new Video</h1>
<form method="POST" action="/videos/add" enctype="multipart/form-data">
    Title:<input type="text" name="title" />
    Video:<input type="file" name="file" accept="video/*" />
    <input type="submit" value="Upload" />
</form>
</body>
</html>

And videos.html to display videos:

<html>
<body>
    <h1>View Video</h1>
    Title: <span th:text="${title}">title</span>
    <video width="400" controls>
        <source th:src="${url}" />
    </video>
</body>
</html>

8. Conclusion

In this article, we learned how to upload and retrieve files using MongoDB and Spring Boot. We used both BSON and GridFS to upload and retrieve files.

As always, the full source code is available in the GitHub project.