Building an OSS Spring Boot Starter Using Amazon S3
This article provides a step‑by‑step guide to creating a Spring Boot starter for enterprise‑grade object storage services, explaining OSS concepts, Amazon S3 basics, project setup, Maven dependencies, configuration classes, template interfaces, implementation details, auto‑configuration, packaging, and testing.
What is OSS?
Object Storage Service (OSS) is a cloud‑based storage solution that uses HTTP APIs to store and retrieve objects, offering features such as versioning, permission control, and lifecycle management.
OSS in Projects
OSS is essential for managing images, files, audio, and other objects in modern applications, providing upload, download, preview, and delete capabilities.
What is Amazon S3?
Amazon Simple Storage Service (S3) is the original cloud storage service whose protocol has become the de‑facto standard for object storage, offering a unified REST/SOAP interface, unlimited storage, high throughput, versioning, and access control.
Why Build a Spring Boot Starter Based on Amazon S3?
Since most OSS providers (Alibaba Cloud OSS, Tencent COS, Qiniu, MinIO, etc.) support the S3 protocol, a unified starter simplifies integration, migration, and extensibility across different vendors.
Create a Spring Boot Project
Generate a new Spring Boot project named oss-spring-boot-starter and add the required dependencies.
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-s3 -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.12.423</version>
</dependency>Project pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.9</version>
<relativePath/>
</parent>
<groupId>com.qing</groupId>
<artifactId>oss-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>oss-spring-boot-starter</name>
<description>Demo oss-spring-boot-starter</description>
<properties>
<java.version>1.8</java.version>
<aws.version>1.12.423</aws.version>
<hutool.version>5.8.5</hutool.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-s3 -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>${aws.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>Write OssProperties
Define configuration properties bound to the oss prefix.
oss.endpoint=xxx
oss.accessKey=xxx
oss.secretKey=xxx
/**
* @Author 公众号:架构师指南
* @Description Oss配置类
* @ClassName OssProperties
* @Date 2023/3/18 17:51
*/
@Data
@ConfigurationProperties(prefix = "oss")
public class OssProperties {
/** 对象存储服务的URL */
private String endpoint;
/** 区域 */
private String region;
/** path‑style 访问模式 */
private Boolean pathStyleAccess = true;
/** Access key */
private String accessKey;
/** Secret key */
private String secretKey;
/** 最大线程数,默认 100 */
private Integer maxConnections = 100;
}Create OssTemplate Interface
Define the contract for OSS operations.
/**
* @Author 公众号:架构师指南
* @Description oss操作模板
* @ClassName OssTemplate
* @Date 2023/3/18 18:15
*/
public interface OssTemplate {
/** 创建 bucket */
void createBucket(String bucketName);
/** 获取所有 bucket */
List
getAllBuckets();
/** 删除 bucket */
void removeBucket(String bucketName);
/** 上传文件(带 content type) */
void putObject(String bucketName, String objectName, InputStream stream, String contextType) throws Exception;
/** 上传文件(默认 content type) */
void putObject(String bucketName, String objectName, InputStream stream) throws Exception;
/** 获取对象 */
S3Object getObject(String bucketName, String objectName);
/** 获取对象 URL */
String getObjectURL(String bucketName, String objectName, Integer expires);
/** 删除对象 */
void removeObject(String bucketName, String objectName) throws Exception;
/** 按前缀查询对象 */
List
getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive);
}Implement OssTemplate
Use the Amazon S3 Java SDK to realize the methods.
/**
* @Author 公众号:架构师指南
* @Description OssTemplate的实现类
* @ClassName OssTemplateImpl
* @Date 2023/3/18 19:02
*/
@RequiredArgsConstructor
public class OssTemplateImpl implements OssTemplate {
private final AmazonS3 amazonS3;
@Override
@SneakyThrows
public void createBucket(String bucketName) {
if (!amazonS3.doesBucketExistV2(bucketName)) {
amazonS3.createBucket(bucketName);
}
}
@Override
@SneakyThrows
public List
getAllBuckets() {
return amazonS3.listBuckets();
}
@Override
@SneakyThrows
public void removeBucket(String bucketName) {
amazonS3.deleteBucket(bucketName);
}
@Override
@SneakyThrows
public void putObject(String bucketName, String objectName, InputStream stream, String contextType) {
putObject(bucketName, objectName, stream, stream.available(), contextType);
}
@Override
@SneakyThrows
public void putObject(String bucketName, String objectName, InputStream stream) {
putObject(bucketName, objectName, stream, stream.available(), "application/octet-stream");
}
@Override
@SneakyThrows
public S3Object getObject(String bucketName, String objectName) {
return amazonS3.getObject(bucketName, objectName);
}
@Override
@SneakyThrows
public String getObjectURL(String bucketName, String objectName, Integer expires) {
Date date = new Date();
Calendar calendar = new GregorianCalendar();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, expires);
URL url = amazonS3.generatePresignedUrl(bucketName, objectName, calendar.getTime());
return url.toString();
}
@Override
@SneakyThrows
public void removeObject(String bucketName, String objectName) {
amazonS3.deleteObject(bucketName, objectName);
}
@Override
@SneakyThrows
public List
getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix);
return objectListing.getObjectSummaries();
}
@SneakyThrows
private PutObjectResult putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) {
byte[] bytes = IOUtils.toByteArray(stream);
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(size);
objectMetadata.setContentType(contextType);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
return amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata);
}
}Create OssAutoConfiguration
Auto‑configure the AmazonS3 client and the OssTemplate bean.
/**
* @Author 公众号:架构师指南
* @Description oss配置bean
* @ClassName OssAutoConfiguration
* @Date 2023/3/18 18:23
*/
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(OssProperties.class)
public class OssAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public AmazonS3 ossClient(OssProperties ossProperties) {
ClientConfiguration clientConfiguration = new ClientConfiguration();
clientConfiguration.setMaxConnections(ossProperties.getMaxConnections());
AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(
ossProperties.getEndpoint(), ossProperties.getRegion());
AWSCredentials awsCredentials = new BasicAWSCredentials(ossProperties.getAccessKey(), ossProperties.getSecretKey());
AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
return AmazonS3Client.builder()
.withEndpointConfiguration(endpointConfiguration)
.withClientConfiguration(clientConfiguration)
.withCredentials(awsCredentialsProvider)
.disableChunkedEncoding()
.withPathStyleAccessEnabled(ossProperties.getPathStyleAccess())
.build();
}
@Bean
@ConditionalOnBean(AmazonS3.class)
public OssTemplate ossTemplate(AmazonS3 amazonS3) {
return new OssTemplateImpl(amazonS3);
}
}Create spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.qing.oss.OssAutoConfigurationPackage and Install
Remove the default Spring Boot Maven plugin from the pom to avoid install errors, then run mvn install to publish the starter to the local repository.
Testing
Create a separate Spring Boot test project, add the starter dependency, configure oss.endpoint , oss.accessKey , and oss.secretKey , and write a simple test case.
@SpringBootTest
class TestOssSpringBootStarterApplicationTests {
@Autowired
private OssTemplate ossTemplate;
@Test
void contextLoads() {
ossTemplate.createBucket("oss02");
}
}Conclusion
The article demonstrates how to build a reusable Spring Boot starter for enterprise OSS services, leveraging the Amazon S3 protocol to support Alibaba Cloud OSS, Tencent COS, Qiniu, MinIO, and other compatible providers, enabling quick integration and future extensibility.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.