File Upload, Download, and Web Extensions in Spring Boot 3

Introduction

Most backend services need more than CRUD JSON APIs. You often need file upload/download, request interception, and cross-cutting web hooks for logging, authentication context, and request lifecycle tracking. In this chapter, you will implement practical file endpoints and understand when to use interceptors, filters, and listeners in Spring Boot 3.

Prerequisites

  • Completed:
    • 06_spring_mvc_basics_annotations_and_routing.md
    • 07_validation_and_global_exception_handling.md
  • A running Spring Boot 3 project with spring-boot-starter-web

Multipart Upload Basics

Spring Boot supports file upload through MultipartFile.

Configure upload limits

In application.yml:

yaml
spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 20MB

Equivalent in .properties:

properties
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=20MB

Single File Upload Endpoint

Request example:

bash
curl -X POST http://localhost:8080/api/files/upload \
  -F "file=@/path/to/avatar.png"

Multiple File Upload

For batch upload:

java
@PostMapping("/upload-batch")
public ResponseEntity<Map<String, Object>> uploadBatch(@RequestParam("files") MultipartFile[] files) {
    return ResponseEntity.ok(Map.of("count", files.length));
}

You can iterate and reuse the single-file storage logic.

File Download Endpoint

Security Notes for File APIs

File APIs are common attack surfaces. Protect them with:

  • file size limits
  • extension allowlist (for example only .png, .jpg, .pdf)
  • storage path normalization and traversal checks
  • virus scanning (for enterprise scenarios)

Warning

Path Traversal Risk

Never trust raw file names from clients. Always normalize paths and prevent ../ traversal outside the upload root.

Serve Static Resources

By default, Spring Boot serves static files from:

  • src/main/resources/static
  • src/main/resources/public

Example:

text
src/main/resources/static/logo.png

Then accessible at:

text
GET /logo.png

For user-uploaded files, avoid exposing arbitrary disk paths directly. Prefer controlled download endpoints.

Interceptor vs Filter vs Listener

These are common extension points in web apps:

ComponentLayerTypical use
FilterServlet container levelCORS, encoding, low-level request wrapping
InterceptorSpring MVC levelauth checks, request timing, user context in MVC flow
ListenerServlet lifecycle eventsstartup hooks, session lifecycle tracking

Create a Simple Interceptor

Register it:

Simple Filter Example

Filters execute before MVC routing, so they are ideal for generic request/response concerns.

Common Upload/Download Errors

Maximum upload size exceeded

  • Increase multipart limits in config
  • Confirm reverse proxy (for example Nginx) allows same size

Download returns 404 but file exists

  • Check storage path and working directory
  • Log absolute path to verify expected location

Uploaded filename appears garbled

  • Set proper request encoding and test with UTF-8 filenames

Best Practices

  • Store uploaded files with generated names (UUID), keep original name as metadata
  • Separate file metadata (DB) and binary storage (disk/object storage)
  • Use object storage (S3/OSS/MinIO) for scalable production systems
  • Add auth checks for upload/download endpoints
  • Audit upload and download operations

Quick Recap

In this chapter, you learned:

  1. Multipart upload config and endpoints
  2. Controlled file download responses
  3. Static resource behavior
  4. Core differences between interceptor/filter/listener
  5. Practical extension patterns for request processing

Next chapter will cover content negotiation and custom HttpMessageConverter support for multiple response formats.

FAQ

Should files be stored in the database?

Small files can be, but most systems store file binaries in object storage and keep metadata in relational tables.

Interceptor or filter for authentication?

Both can work. In Spring MVC APIs, interceptors are often simpler for route-level logic. Security frameworks (Spring Security) usually handle auth in filter chains.

Can I directly expose /uploads as static files?

Possible, but risky unless you strictly control access and file types. Controlled download endpoints are safer.

Why use UUID file names?

To avoid collisions and reduce risks from user-provided names.

Do I need listener in every project?

No. Use listeners when you need app/session lifecycle hooks; many APIs can run without custom listeners.