Chapter 5.4☕ 16 min read

File Upload and Download

Upload resumes, download PDFs. Files made easy.

01The Concept: Multipart Requests

The Hyderabad Metro Luggage Check Analogy:

When you carry a backpack on the Hyderabad Metro, the security scanner doesn’t look inside your pockets. It scans the whole bag as one unit.

Normal HTTP requests send data as a single JSON body. But when uploading files, the browser sends a “Multipart/Form-Data” request. It splits the HTTP body into different “parts” — one part for the file, one part for the text fields. Spring’s MultipartFile reads these specific parts.

02Technical Explanation
  1. MultipartFile: An interface representing an uploaded file. It provides methods like getOriginalFilename(), getBytes(), and transferTo().
  2. application.properties: You must configure limits for file sizes to prevent attackers from crashing your server with a 10GB file.
  3. ResponseEntity<Resource>: The standard way to download a file. Resource represents the actual file bytes.
03Full Working Code: Uploading Files
# Max file size allowed
spring.servlet.multipart.max-file-size=10MB
# Max total request size allowed
spring.servlet.multipart.max-request-size=10MB
package com.devinhyderabad;

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@RestController
@RequestMapping("/api/files")
public class FileController {

private static final String UPLOAD_DIR = "uploads/";

@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("File is empty");
}
try {
Path uploadPath = Paths.get(UPLOAD_DIR);
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
Path filePath = uploadPath.resolve(file.getOriginalFilename());
file.transferTo(filePath.toFile());
return ResponseEntity.ok("File uploaded successfully: " + file.getOriginalFilename());
} catch (IOException e) {
return ResponseEntity.internalServerError().body("Failed to upload file");
}
}
}
04Full Working Code: Downloading Files
    @GetMapping("/download/{filename}")
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
try {
Path filePath = Paths.get(UPLOAD_DIR).resolve(filename).normalize();
Resource resource = new UrlResource(filePath.toUri());

if (resource.exists() && resource.isReadable()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="" + resource.getFilename() + """)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (IOException e) {
return ResponseEntity.internalServerError().build();
}
}
}
05Why It Matters / Interview Note

Interview Question: “Where should you store uploaded files in an enterprise application?”

Answer: For local development, storing files on the local disk is fine. In production (like AWS/Docker), local disk is ephemeral and gets wiped on restart. Enterprise apps store files in cloud object storage like Amazon S3, Azure Blob Storage, or a database BLOB column.

Enterprise Note: Always sanitize the filename before saving it. file.getOriginalFilename() can include path traversal strings like ../../etc/passwd. Use Paths.get(name).getFileName().toString() to strip directory paths.

Key Takeaways

  • ✅ MultipartFile handles file uploads with getOriginalFilename(), getBytes(), transferTo()
  • ✅ Always set max file size limits in application.properties to prevent abuse
  • ✅ ResponseEntity<Resource> is the standard way to download files
  • ✅ Always sanitize filenames to prevent path traversal attacks