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
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>
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
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;
}
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 Method | Path | Description |
|---|---|---|
| POST | /api/files | Upload file |
| GET | /api/files | List 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()));
}
}
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
| Problem | Cause | Fix |
|---|---|---|
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.