Spring Boot File Upload & Download REST API — Store Files in MySQL/Oracle (Step-by-Step Guide)

Spring Boot File Upload to Database and Download REST API (MySQL & Oracle)

A very common requirement in backend applications is: upload a file from a web or mobile client, store it safely in a database, and later download it via REST API.

In this step-by-step guide, we’ll build a Spring Boot 3 REST API that:

  • Accepts file uploads via MultipartFile
  • Stores file content as a BLOB in a relational database (MySQL or Oracle)
  • Exposes endpoints to list files and download a file by ID
  • Includes clean error handling and simple best practices
Goal: After following this guide, even if you are new to Spring Boot, you’ll have a working project where you can upload a file, see it stored in DB, and download it back.

1. Project Setup (Spring Boot 3 + Maven)

1.1. Create a Spring Boot project

You can use start.spring.io with:

  • Project: Maven
  • Language: Java
  • Spring Boot: 3.x.x
  • Dependencies: Spring Web, Spring Data JPA, MySQL Driver (or Oracle driver)

For this tutorial, we will show MySQL properties as the primary example and also Oracle configuration as an alternative. Pick the one you actually use.

1.2. Maven dependencies

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

  <!-- JPA -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

  <!-- MySQL Driver (primary example) -->
  <dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
  </dependency>

  <!-- OR: Oracle JDBC driver (optional, if you use Oracle) -->
  <!--
  <dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc11</artifactId>
    <scope>runtime</scope>
  </dependency>
  -->

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

  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
  </dependency>
</dependencies>
If you don’t want to use Lombok, you can remove it and write getters/setters manually.

2. Database Configuration (MySQL or Oracle)

Create a database first in MySQL / Oracle (for example file_storage). Then configure application.properties (or application.yml).

2.1. MySQL configuration (primary)

spring.datasource.url=jdbc:mysql://localhost:3306/file_storage?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=your_password

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect

2.2. Oracle configuration (alternative)

spring.datasource.url=jdbc:oracle:thin:@localhost:1521/ORCLCDB
spring.datasource.username=FILE_USER
spring.datasource.password=your_password

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
Make sure the DB user has rights to create tables and insert BLOB data.

3. Designing the File Entity (Store Content as BLOB)

We will store the file itself in a BLOB column, plus some useful metadata like file name, content type, and size.

3.1. FileEntity.java

package com.example.filestorage.entity;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name = "stored_file")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class FileEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(nullable = false)
  private String fileName;

  @Column(nullable = false)
  private String contentType;

  @Column(nullable = false)
  private Long size;

  @Lob
  @Column(nullable = false)
  private byte[] data;
}
@Lob tells JPA that data should be mapped to a BLOB (binary large object) column. In MySQL this will typically become LONGBLOB, in Oracle a BLOB.

4. Repository Layer

4.1. FileRepository.java

package com.example.filestorage.repository;

import com.example.filestorage.entity.FileEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface FileRepository extends JpaRepository<FileEntity, Long> {
}

5. Service Layer (Upload & Download Logic)

The service layer is responsible for converting MultipartFile to FileEntity, saving it, and loading it back for download.

5.1. FileService.java

package com.example.filestorage.service;

import com.example.filestorage.entity.FileEntity;
import com.example.filestorage.repository.FileRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

@Service
@RequiredArgsConstructor
public class FileService {

  private final FileRepository fileRepository;

  public FileEntity storeFile(MultipartFile file) {
    try {
      FileEntity entity = FileEntity.builder()
          .fileName(file.getOriginalFilename())
          .contentType(file.getContentType())
          .size(file.getSize())
          .data(file.getBytes())
          .build();

      return fileRepository.save(entity);
    } catch (IOException ex) {
      throw new RuntimeException("Could not store file: " + file.getOriginalFilename(), ex);
    }
  }

  public FileEntity getFile(Long id) {
    return fileRepository.findById(id)
        .orElseThrow(() -> new RuntimeException("File not found: " + id));
  }

  public List<FileEntity> listFiles() {
    return fileRepository.findAll();
  }
}

6. DTOs for API Responses

For listing files, we don’t want to send the raw bytes. We’ll expose only metadata like ID, name, size, etc.

6.1. FileResponse.java

package com.example.filestorage.dto;

import lombok.*;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class FileResponse {

  private Long id;
  private String fileName;
  private String contentType;
  private Long size;
}

7. REST Controller (Upload, List, Download)

We’ll expose three endpoints:

HTTP MethodPathDescription
POST/api/filesUpload file
GET/api/filesList file metadata
GET/api/files/{id}Download file by ID

7.1. FileController.java

package com.example.filestorage.controller;

import com.example.filestorage.dto.FileResponse;
import com.example.filestorage.entity.FileEntity;
import com.example.filestorage.service.FileService;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

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

  private final FileService fileService;

  @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  public ResponseEntity<FileResponse> uploadFile(@RequestPart("file") MultipartFile file) {
    FileEntity saved = fileService.storeFile(file);

    FileResponse response = FileResponse.builder()
        .id(saved.getId())
        .fileName(saved.getFileName())
        .contentType(saved.getContentType())
        .size(saved.getSize())
        .build();

    return ResponseEntity.ok(response);
  }

  @GetMapping
  public ResponseEntity<List<FileResponse>> listFiles() {
    List<FileResponse> files = fileService.listFiles().stream()
        .map(entity -> FileResponse.builder()
            .id(entity.getId())
            .fileName(entity.getFileName())
            .contentType(entity.getContentType())
            .size(entity.getSize())
            .build())
        .toList();

    return ResponseEntity.ok(files);
  }

  @GetMapping("/{id}")
  public ResponseEntity<ByteArrayResource> downloadFile(@PathVariable Long id) {
    FileEntity entity = fileService.getFile(id);

    return ResponseEntity.ok()
        .contentType(MediaType.parseMediaType(entity.getContentType()))
        .header(HttpHeaders.CONTENT_DISPOSITION,
            "attachment; filename=\"" + entity.getFileName() + "\"")
        .body(new ByteArrayResource(entity.getData()));
  }
}
Important: Note the consumes = MediaType.MULTIPART_FORM_DATA_VALUE on the upload method. Without it, some clients may not send the file correctly.

8. Testing the API with cURL or Postman

8.1. Upload a file (Windows)

curl -X POST "http://localhost:8080/api/files" ^
  -H "Content-Type: multipart/form-data" ^
  -F "file=@C:/temp/sample.pdf"

8.2. Upload a file (Linux/macOS)

curl -X POST "http://localhost:8080/api/files" \
  -H "Content-Type: multipart/form-data" \
  -F "file=@/tmp/sample.pdf"

8.3. List files

curl http://localhost:8080/api/files
Example JSON:
[
  {
    "id": 1,
    "fileName": "sample.pdf",
    "contentType": "application/pdf",
    "size": 45231
  }
]

8.4. Download a file

curl -X GET "http://localhost:8080/api/files/1" -o downloaded.pdf

9. Common Pitfalls and How to Avoid Them

ProblemCauseFix
413 Payload Too Large File size exceeds server limits Configure spring.servlet.multipart.max-file-size and max-request-size
Could not store file exception DB column too small for BLOB Use a proper BLOB/LONGBLOB column; let JPA generate schema or tune DDL
OutOfMemoryError Huge files stored as BLOB in a single JVM Set reasonable file size limits, consider storing large files on disk or object storage

9.1. Multipart file size configuration

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

10. When to Store Files in DB vs File System

  • Store in DB (BLOB) when:
    • You want strong consistency with business data
    • You have moderate file sizes (e.g., up to a few MB)
    • Backups are already DB-centric
  • Store on File System / Object Storage when:
    • Files are large (tens/hundreds of MB or more)
    • You need CDNs, range requests, streaming
    • You don’t want DB size to explode

This tutorial demonstrates file-in-DB because it keeps the example simple and portable to both MySQL and Oracle. In a real system, you might combine both approaches depending on use case.


11. Summary

We built a complete Spring Boot file upload & download API that:

  • Accepts a file using MultipartFile
  • Stores it as a BLOB in MySQL / Oracle
  • Exposes endpoints for listing and downloading files
  • Handles metadata like content type and size

You can now integrate this API with a frontend, add authentication (Spring Security + JWT), and introduce validation rules such as allowed MIME types and size limits.