Spring Boot File Upload and Download REST API Tutorial

Spring Boot File Upload and Download REST API Tutorial

Introduction

File upload and download functionality is a common requirement in modern web applications.

Applications may need to allow users to upload:

  • Profile pictures
  • Documents
  • PDFs
  • Product images
  • Invoices
  • Reports
  • Other files

Spring Boot makes it relatively simple to implement file handling through Spring MVC and the MultipartFile interface.

In this tutorial, we will build a simple Spring Boot File Upload and Download REST API.

You will learn how to:

  • Upload a file using MultipartFile
  • Validate uploaded files
  • Store files on the server
  • Download files through a REST endpoint
  • Handle missing files
  • Restrict file size
  • Return appropriate HTTP responses
  • Apply file-upload security best practices

What is MultipartFile in Spring Boot?

MultipartFile is an interface provided by Spring that represents an uploaded file received through a multipart HTTP request.

It provides useful methods such as:

getOriginalFilename()
getContentType()
getSize()
getBytes()
getInputStream()

These methods allow your application to inspect and process uploaded files.


How File Upload Works

The basic process looks like this:

User Selects File
       ↓
HTTP Multipart Request
       ↓
Spring Boot Controller
       ↓
MultipartFile
       ↓
Validate File
       ↓
Store File
       ↓
Return Response

For downloading:

Client Request
       ↓
Download Endpoint
       ↓
Find File
       ↓
Read File
       ↓
HTTP Response
       ↓
Downloaded File

Create a Spring Boot Project

For this example, create a Spring Boot project with Spring Web.

The main dependency is:

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

If you are using Spring Boot with Maven, place this inside the <dependencies> section of your pom.xml.


Create a File Storage Service

It is better to keep file-storage logic outside the controller.

Create:

FileStorageService.java

Example:

@Service
public class FileStorageService {

    private final Path uploadPath =
            Paths.get("uploads");

    public FileStorageService() throws IOException {

        Files.createDirectories(uploadPath);
    }

    public String storeFile(MultipartFile file)
            throws IOException {

        String fileName =
                StringUtils.cleanPath(
                        file.getOriginalFilename());

        Path targetLocation =
                uploadPath.resolve(fileName);

        Files.copy(
                file.getInputStream(),
                targetLocation,
                StandardCopyOption.REPLACE_EXISTING);

        return fileName;
    }
}

This service creates an uploads directory and stores uploaded files inside it.


Create the File Upload Controller

Now create:

FileController.java

Example:

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

    private final FileStorageService
            fileStorageService;

    public FileController(
            FileStorageService fileStorageService) {

        this.fileStorageService =
                fileStorageService;
    }

    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(
            @RequestParam("file")
            MultipartFile file) {

        try {

            String fileName =
                    fileStorageService
                    .storeFile(file);

            return ResponseEntity.ok(
                    "File uploaded successfully: "
                    + fileName);

        } catch (IOException ex) {

            return ResponseEntity
                    .internalServerError()
                    .body("Could not upload file.");
        }
    }
}

Test the File Upload API

You can test the endpoint using Postman or another API client.

HTTP Method

POST

Endpoint

/api/files/upload

Select:

Body → form-data

Create a field named:

file

Change its type from Text to File, then select a file from your computer.


Example Response

A successful upload could return:

File uploaded successfully: employee-profile.jpg

Why Use form-data?

Files are normally uploaded using:

multipart/form-data

rather than sending the file as ordinary JSON.

A multipart request can contain both files and additional form fields.

For example:

file → employee.jpg
name → Muhammad Zeeshan
department → IT

Upload a File with Additional Data

You can accept additional request parameters along with the file.

@PostMapping("/upload")
public ResponseEntity<String> uploadFile(
        @RequestParam("file")
        MultipartFile file,

        @RequestParam("description")
        String description) {

    // File processing

    return ResponseEntity.ok(
            "File uploaded successfully");
}

This allows a request to contain both file data and regular form values.


Validate the Uploaded File

Never blindly accept every uploaded file.

At minimum, check whether the file exists.

if (file.isEmpty()) {

    return ResponseEntity
            .badRequest()
            .body("Please select a file.");
}

You should also consider checking:

  • File size
  • File extension
  • MIME type
  • Filename
  • Storage location

Check File Size

Example:

long maxSize = 5 * 1024 * 1024;

if (file.getSize() > maxSize) {

    return ResponseEntity
            .badRequest()
            .body("File size exceeds 5 MB.");
}

Here, the maximum file size is approximately 5 MB.


Check the Content Type

You can inspect the uploaded file's MIME type.

String contentType =
        file.getContentType();

For example:

image/jpeg
image/png
application/pdf

However, don't rely on the client-provided content type alone for security-sensitive applications.


Configure Maximum Upload Size

Spring Boot allows multipart upload limits to be configured in:

application.properties

Example:

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

The first property limits an individual uploaded file.

The second limits the overall multipart request.


Generate a Unique Filename

One important problem with file uploads is filename collision.

For example, two users could upload:

profile.jpg

Instead of using the original filename directly, generate a unique name.

Example:

String fileName =
        UUID.randomUUID()
        + "-"
        + StringUtils.cleanPath(
              file.getOriginalFilename());

The result could look like:

8b7d9f2a-profile.jpg

This reduces the chance of overwriting another file.


Download a File

Now let's create a download endpoint.

@GetMapping("/download/{fileName}")
public ResponseEntity<Resource>
        downloadFile(
        @PathVariable String fileName)
        throws IOException {

    Path filePath =
            Paths.get("uploads")
                 .resolve(fileName)
                 .normalize();

    Resource resource =
            new UrlResource(
                filePath.toUri());

    if (!resource.exists()) {

        return ResponseEntity
                .notFound()
                .build();
    }

    return ResponseEntity.ok()
            .body(resource);
}

This endpoint reads the requested file and returns it as a resource.


Improve the Download Response

For a production REST API, you should provide an appropriate content type.

String contentType =
        Files.probeContentType(
            filePath);

Then:

return ResponseEntity.ok()
        .contentType(
            MediaType.parseMediaType(
                contentType))
        .body(resource);

You can also tell the browser to download the file instead of displaying it.

.header(
    HttpHeaders.CONTENT_DISPOSITION,
    "attachment; filename=\"" +
    resource.getFilename() + "\"")

Complete Download Example

@GetMapping("/download/{fileName}")
public ResponseEntity<Resource>
        downloadFile(
        @PathVariable String fileName)
        throws IOException {

    Path filePath =
            Paths.get("uploads")
                 .resolve(fileName)
                 .normalize();

    Resource resource =
            new UrlResource(
                filePath.toUri());

    if (!resource.exists()) {

        return ResponseEntity
                .notFound()
                .build();
    }

    String contentType =
            Files.probeContentType(
                filePath);

    if (contentType == null) {
        contentType =
                "application/octet-stream";
    }

    return ResponseEntity.ok()
            .contentType(
                MediaType.parseMediaType(
                    contentType))
            .header(
                HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" +
                resource.getFilename() +
                "\"")
            .body(resource);
}

File Upload and Download Architecture

                 CLIENT
                    │
          ┌─────────┴─────────┐
          ↓                   ↓
      Upload File        Download File
          ↓                   ↓
       Controller          Controller
          ↓                   ↓
      Validation          File Lookup
          ↓                   ↓
       Service             Resource
          ↓                   ↓
      File Storage        HTTP Response

Where Should Uploaded Files Be Stored?

There are several options.

Local File System

Good for:

  • Learning projects
  • Small applications
  • Development

Example:

/uploads

Database

Files can be stored as binary data, although this isn't always the best choice for large files.

Cloud Object Storage

Production applications often use object storage services for uploaded files.

Examples include:

  • Amazon S3
  • Google Cloud Storage
  • Azure Blob Storage

For a large-scale application, separating file storage from the application server can make scaling easier.


File Upload Security Best Practices

File uploads can create serious security risks if implemented incorrectly.

Never Trust the Original Filename

Don't use user-provided filenames directly as storage paths.

Generate Unique Names

Use UUIDs or another safe naming strategy.

Restrict File Types

Only allow file types your application actually needs.

Limit File Size

Large uploads can consume server resources.

Store Files Outside Executable Directories

Uploaded content should not be treated as executable application code.

Validate File Content

For security-sensitive applications, don't rely solely on filename extensions or client-provided MIME types.

Protect Download Endpoints

If files contain private information, require authentication and authorization before allowing downloads.


Example API Endpoints

MethodEndpointPurpose
POST/api/files/uploadUpload a file
GET/api/files/download/{fileName}Download a file

Common Errors

File Size Exceeded

If the uploaded file exceeds the configured limit, Spring Boot can reject the request.

Make sure your multipart configuration is appropriate for your application.


File Not Found

Return:

404 Not Found

when the requested file doesn't exist.


Invalid File Type

Return:

400 Bad Request

when the uploaded file doesn't meet your application's requirements.


File Upload with Authentication

If you've followed our Spring Security and JWT tutorials, you can protect the upload endpoint.

For example:

.requestMatchers(
    "/api/files/upload")
.hasRole("USER")

Now only authenticated users with the appropriate role can upload files.

This connects our file-upload tutorial with the security content cluster.


Best Practices for Production

For a production application:

  1. Validate every upload.
  2. Limit file size.
  3. Generate unique filenames.
  4. Avoid trusting user-provided paths.
  5. Protect private files.
  6. Use HTTPS.
  7. Log upload failures.
  8. Consider cloud object storage for large applications.
  9. Scan files when your application's security requirements call for malware detection.
  10. Never allow uploaded content to execute as application code.

Frequently Asked Questions

What is MultipartFile in Spring Boot?

MultipartFile represents a file uploaded through a multipart HTTP request.

How do I upload a file in Spring Boot?

Create a POST endpoint and receive the file with @RequestParam("file") MultipartFile file.

What is multipart/form-data?

It is an HTTP content type commonly used when sending files and form fields in the same request.

Can Spring Boot upload multiple files?

Yes. Multiple MultipartFile values can be accepted by an endpoint.

Where should files be stored?

For simple applications, local storage can be sufficient. Production applications may benefit from dedicated object storage.

Should uploaded files be stored in MySQL?

It depends on the application. For many applications, storing file metadata in MySQL and the actual files in dedicated storage is more practical for large files.


Conclusion

Spring Boot offers a straightforward approach to implementing file upload and download functionality using MultipartFile, Spring MVC, and REST endpoints.

A production-ready implementation should go beyond simply accepting a file. File size, file type, filename handling, storage, authentication, authorization, and security must all be considered.

Once you understand these concepts, you can extend the API to support profile pictures, documents, invoices, product images, and other real-world file-management requirements.

Continue Learning Spring Boot

Muhammad Zeeshan Haider

Hello, I'm Muhammad Zeeshan, a Java Developer, Spring Boot Educator, and Technical Blogger. Through Java Web Action, I share practical tutorials, guides, and real-world examples on Java, Spring Boot, Microservices, REST APIs, JPA, Hibernate, MySQL, and software architecture. My goal is to help developers learn modern Java technologies and build professional applications with confidence.

Post a Comment

Please enter relevant questions and information.

Previous Post Next Post