Implementing File Storage and Handling
1. Configuring File Upload Properties
| Property |
Default |
spring.servlet.multipart.enabled |
true |
spring.servlet.multipart.max-file-size |
1MB |
spring.servlet.multipart.max-request-size |
10MB |
spring.servlet.multipart.location |
System temp |
spring.servlet.multipart.file-size-threshold |
0 (always disk) |
2. Handling Multipart File Uploads
Example: Reactive multipart file info
@PostMapping(value="/upload", consumes=MediaType.MULTIPART_FORM_DATA_VALUE)
public Map<String,Object> upload(@RequestPart("file") MultipartFile file) throws IOException {
return Map.of("name", file.getOriginalFilename(),
"size", file.getSize(),
"type", file.getContentType());
}
3. Saving Files to Local Filesystem
Example: Save file with path traversal guard
@Service
public class FileStorageService {
private final Path root = Path.of("/var/data/uploads");
public Path save(MultipartFile file) throws IOException {
Files.createDirectories(root);
Path target = root.resolve(uniqueName(file.getOriginalFilename())).normalize();
if (!target.startsWith(root)) throw new SecurityException("Path traversal");
file.transferTo(target);
return target;
}
}
Warning: Always validate the resolved path stays within the storage root to
prevent path traversal attacks.
4. Generating Unique Filenames (UUID)
| Strategy |
Example |
| UUID + ext |
uuid + "." + ext |
| Timestamp + random |
System.currentTimeMillis() + "-" + random |
| Content hash |
SHA-256 → dedup |
5. Serving Static Files
| Location |
URL |
classpath:/static/ |
/ |
classpath:/public/ |
/ |
classpath:/resources/ |
/ |
classpath:/META-INF/resources/ |
/ |
Example: Serve uploaded files as static resource
@Override
public void addResourceHandlers(ResourceHandlerRegistry r) {
r.addResourceHandler("/files/**").addResourceLocations("file:/var/data/uploads/");
}
6. Implementing File Download Endpoints
@GetMapping("/download/{id}")
public ResponseEntity<Resource> download(@PathVariable String id) throws IOException {
Path file = storage.locate(id);
Resource resource = new UrlResource(file.toUri());
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFileName() + "\"")
.contentLength(Files.size(file))
.body(resource);
}
7. Streaming Large Files
Example: Stream large file to response
@GetMapping("/stream/{id}")
public ResponseEntity<StreamingResponseBody> stream(@PathVariable String id) {
StreamingResponseBody body = out -> {
try (InputStream in = storage.openStream(id)) { in.transferTo(out); }
};
return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).body(body);
}
8. Integrating AWS S3 for Cloud Storage
Example: Upload to S3 and generate presigned URL
@Service
public class S3Storage {
private final S3Client s3;
public String upload(String key, MultipartFile file) throws IOException {
s3.putObject(b -> b.bucket("app-uploads").key(key).contentType(file.getContentType()),
RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
return key;
}
public URL presignedGet(String key, Duration ttl) {
try (S3Presigner p = S3Presigner.create()) {
return p.presignGetObject(r -> r.signatureDuration(ttl)
.getObjectRequest(g -> g.bucket("app-uploads").key(key))).url();
}
}
}
9. Using Spring Content for File Management
| Module |
Purpose |
spring-content-fs |
Filesystem store |
spring-content-s3 |
AWS S3 store |
spring-content-rest |
HTTP endpoints (PUT/GET/DELETE) |
@ContentId/@ContentLength |
Entity associations |
10. Validating File Types and Sizes
Example: Validate file type and size
private static final Set<String> ALLOWED = Set.of("image/png", "image/jpeg", "application/pdf");
private void validate(MultipartFile f) {
if (f.isEmpty()) throw new BadRequestException("Empty file");
if (f.getSize() > 10 * 1024 * 1024) throw new BadRequestException("> 10MB");
String type = Optional.ofNullable(f.getContentType()).orElse("");
if (!ALLOWED.contains(type)) throw new BadRequestException("Type not allowed");
// Magic bytes check (Apache Tika) for true content type
}
11. Implementing File Compression
Example: Stream files as ZIP archive
@GetMapping("/zip")
public void zip(HttpServletResponse res) throws IOException {
res.setContentType("application/zip");
res.setHeader("Content-Disposition", "attachment; filename=files.zip");
try (ZipOutputStream zos = new ZipOutputStream(res.getOutputStream())) {
for (Path file : storage.list()) {
zos.putNextEntry(new ZipEntry(file.getFileName().toString()));
Files.copy(file, zos);
zos.closeEntry();
}
}
}