46

File Uploads and Retrieval in Spring Boot

In contemporary web development, the ability to seamlessly manage file uploads and downloads has transitioned from a specialized feature to…

In contemporary web development, the ability to seamlessly manage file uploads and downloads has transitioned from a specialized feature to a fundamental core requirement. Whether you are building an enterprise document management system, a social media platform, or a simple profile-update service, mastering binary data handling is essential for any robust backend.

This article provides a deep dive into implementing these critical features within the Spring Boot ecosystem. We adopt a “core-to-shell” architectural approach: we begin at the foundational layer by mastering Java NIO (New I/O) operations for efficient file system interaction. From there, we build outward, wrapping those low-level operations in high-level REST controllers and leveraging the MultipartFile interface.

The Core Logic: File Storage

The foundation of any file upload system is the ability to move data from an input stream to a physical location on the server. The Files.copy method from the Java NIO package is the most efficient tool for this task.

Handling Streams: to save the file data.

Files.copy(inputStream //Stream data of file
	, destination //Path of the destination
	, StandardCopyOption.REPLACE_EXISTING); //Copy option

Other kind of storage like S3 buckets can also be used, that will cover in upcoming posts.

Directory Management: Before copying, ensure the destination directory exists by using

Files.createDirectories(destination.getParent())

Implementing the Upload REST Controller

To handle uploads, we use the @PostMapping annotation and the MultipartFile interface, which allows us to intercept the file from an HTTP request.

Basic Upload with Size Validation

It is a best practice to validate file sizes before processing to prevent server strain.

@PostMapping("/upload")
public ResponseEntity<Object> upload(@RequestParam("file") MultipartFile file) {
	// Example: Restricting file size to 5MB programmatically
	if(file.getSize() < 1024 * 1024 * 5) { 
        InputStream inputStream = file.getInputStream();
        // logic to save file...
    }
}

You can also restrict file sizes globally in your application.properties using: spring.servlet.multipart.max-file-size=10MB.


Complete Implementation

Combining validation, directory creation, and error handling results in a production-ready method:

@PostMapping("/upload")
public ResponseEntity<Object> upload(@RequestParam("file") MultipartFile file) {
    if(file.getSize() < 1024 * 1024 * 5) {
        try (InputStream inputStream = file.getInputStream()) {
            Path destinationFile = Paths.get(rootPath).resolve(file.getOriginalFilename()); //rootpath from application.properties
            Files.createDirectories(destinationFile.getParent());
            Files.copy(inputStream, destinationFile, StandardCopyOption.REPLACE_EXISTING);
            log.debug("File uploaded successfully");
            return ResponseEntity.ok().build();
        } catch(IOException ex) {
            return ResponseEntity.internalServerError().body("Error uploading");
        }
    } else {
        return ResponseEntity.badRequest().body("File size limit reached: 5MB");
    }
}

Strategies for Fetching Files

Once files are stored, they need to be served back to the user. There are two primary strategies for this:

  1. Direct Path Access: Useful for publicly available locations.
  2. Binary Stream via REST: Preferred when you need to perform security checks or basic validation before serving the file. Example – React: Fetch a Binary Image with Axios and Display It as Base64

Serving Files as a Resource

To send a file’s binary data back through a REST call, use the UrlResource class.

Resource file = new UrlResource(destination.toUri());
if (!resource.exists() && !resource.isReadable()) {
    return ResponseEntity.internalServerError().body("File not found");
}
return ResponseEntity.ok()
    .contentType(MediaType.parseMediaType("image/jpeg"))
    .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + file.getFilename() + "\"")
    .body(file);

Using Classpath Fallbacks

If a specific user-uploaded file is missing, you can serve a default fallback (like a generic profile picture) from your project’s resource folder:

Resource file = new ClassPathResource("user.jpg");

Ashish Sharma

I’ve always believed that collaboration is the engine of progress. While many say knowledge is power, I believe the true power lies in its distribution. To that end, I am building a curated knowledge base of my professional journey—refined by AI for maximum clarity and depth. Whether you’re here to master a new skill or sharpen an existing one, my goal is to provide a roadmap for your success. This collection will evolve as I do, and I welcome your insights and dialogue as we grow together.

Leave a Reply

Your email address will not be published. Required fields are marked *