Spring Boot + AWS S3: Upload and Download Files
Connecting a Spring Boot application to Amazon S3 is one of the most common real-world backend requirements. Whether you want to store images, documents, logs, or app data, S3 provides secure, scalable, and durable object storage.
In this tutorial, you’ll learn how to integrate Spring Boot with AWS S3 using AWS SDK v2 to build clean and reliable upload/download APIs.
High-Level Architecture
The overall workflow is simple:
- Your Spring Boot application runs locally or on a server.
- The app uses AWS SDK v2 to create an S3Client.
- The S3Client connects to your S3 bucket using AWS credentials.
- REST APIs handle upload and download requests and interact with S3 internally.
This setup works for local development, on-prem servers, and cloud deployments.
Step 1: Create a Spring Boot Project
Create a new project using Spring Initializr:
- Project: Maven
- Language: Java
- Spring Boot: 3.x+
- Dependencies: Spring Web
- Java Version: 17+
Import the project into IntelliJ or your preferred IDE and verify the application starts successfully.
Step 2: Add AWS SDK v2 for S3
Add the following dependency in your pom.xml to communicate with S3:
<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <version>LATEST_VERSION</version> </dependency>
Reload Maven so the dependency downloads.
Step 3: Configure the S3Client Bean
Create a config package and add a class like S3Config. Inside it, define a bean that returns an S3Client.
package com.example.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
@Configuration
public class S3Config {
@Value("${aws.region}")
private String region;
@Value("${aws.accessKey}")
private String accessKey;
@Value("${aws.secretKey}")
private String secretKey;
@Bean
public S3Client s3Client() {
AwsBasicCredentials creds = AwsBasicCredentials.create(accessKey, secretKey);
return S3Client.builder()
.region(Region.of(region))
.credentialsProvider(StaticCredentialsProvider.create(creds))
.build();
}
}
The bean configuration typically loads:
- AWS region
- Access key
- Secret key
- Bucket name (configured separately)
Make sure when creating credentials the order is correct: access key first, secret key second. Reversing them causes errors like: “AWS Access Key ID you provided does not exist in our records.”
Step 4: Service Layer for Upload & Download
Create a service class (for example, S3Service) to encapsulate S3 operations.
Two essential methods:
1. Upload File
Use:
- s3Client.putObject() for upload
- PutObjectRequest for bucket, key, and metadata
- RequestBody.fromBytes(file.getBytes()) to send file content
2. Download File
Use:
- s3Client.getObjectAsBytes() to retrieve file data
- Return the byte[] to the controller
package com.example.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
@Service
public class S3Service {
private final S3Client s3Client;
@Value("${aws.s3.bucketName}")
private String bucketName;
public S3Service(S3Client s3Client) {
this.s3Client = s3Client;
}
// Upload
public String uploadFile(MultipartFile file) throws Exception {
String key = file.getOriginalFilename();
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
s3Client.putObject(
putObjectRequest,
RequestBody.fromBytes(file.getBytes())
);
return "File uploaded successfully: " + key;
}
// Download
public byte[] downloadFile(String key) {
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
ResponseBytes objectBytes = s3Client.getObjectAsBytes(getObjectRequest);
return objectBytes.asByteArray();
}
}
This approach keeps AWS-related logic in a single place.
Step 5: REST Controller for File APIs
Create S3Controller with endpoints:
POST /upload
- Accepts MultipartFile
- Calls s3Service.uploadFile()
- Returns a success message
GET /download
- Accepts key or fileName as a request parameter
- Calls s3Service.downloadFile()
- Returns byte[] with the file content
package com.example.controller;
import com.example.service.S3Service;
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;
@RestController
public class S3Controller {
private final S3Service s3Service;
public S3Controller(S3Service s3Service) {
this.s3Service = s3Service;
}
// Upload API
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) throws Exception {
return s3Service.uploadFile(file);
}
// Download API
@GetMapping("/download")
public ResponseEntity downloadFile(@RequestParam String fileName) {
byte[] data = s3Service.downloadFile(fileName);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(data);
}
}
You can test both endpoints using Postman.
Step 6: Create IAM User & Access Keys
Create a dedicated IAM user for your Spring Boot app:
- Go to IAM → Users → Create user
- Assign S3 permissions (e.g., S3FullAccess)
- Generate an access key (use case: “Local code”)
- Copy Access Key ID and Secret Key safely
These credentials allow your local application to access S3.
Step 7: Configure Application Properties
In application.properties or application.yml, configure:
aws.region=ap-south-1 aws.accessKey=YOUR_ACCESS_KEY aws.secretKey=YOUR_SECRET_KEY aws.s3.bucketName=your-bucket-name
Ensure the bucket name matches exactly — no extra spaces, newlines, or quotes. Incorrect values cause errors like: “The specified bucket is not valid.”
Step 8: Test Upload & Download
Upload Test (POST /upload)
In Postman:
- Create a POST request to
http://localhost:8080/upload - Form-data → key: file → type: File
- Select a sample text/image file
You should see a success message. Verify the file in your S3 bucket.
Download Test (GET /download)
- GET request to
http://localhost:8080/download?fileName=sample-demo.txt - You’ll receive the file bytes in the response
Common Errors & Fixes
“The specified bucket is not valid”
- Check bucket name formatting
- Ensure it matches the region
“AWS Access Key ID you provided does not exist”
- Check access key + secret key order
- Verify the correct IAM user
Security Best Practices
- Never hardcode credentials in source code
- Do not commit access keys to Git
- Prefer environment variables for credentials
- Use IAM roles in production (EC2, ECS, Lambda)
Final Thoughts
By combining Spring Boot, AWS S3, and AWS SDK v2, you can build powerful file upload and download features with minimal code. With the right S3Client configuration, clean service layer, and simple REST APIs, your application becomes production-ready for real-world storage scenarios.
This tutorial sets the foundation for more advanced topics like presigned URLs, multipart uploads, and integrating S3 with CloudFront or Lambda.
Comments
Post a Comment