企業(yè)級的OSS對象存儲服務(wù),這樣封裝萬能好用!
本文主要講解企業(yè)級OSS對象存儲服務(wù)Spring Boot Starter制作,開箱即用,為項目進(jìn)行賦能。基于AmazonS3協(xié)議,適配市面上的對象存儲服務(wù)如:阿里云OSS、騰訊COS、七牛云OSS、MInio等等。
什么是OSS?
OSS(Object Storage Service),對象存儲服務(wù),對象存儲服務(wù)是一種使用HTTP API存儲和檢索對象的工具。就是將系統(tǒng)所要用的文件上傳到云硬盤上,該云硬盤提供了文件下載、上傳、預(yù)覽等一系列服務(wù),具備版本,權(quán)限控制能力,具備數(shù)據(jù)生命周期管理能力這樣的服務(wù)以及技術(shù)可以統(tǒng)稱為OSS
OSS在項目中的使用
OSS對象存儲在目前大部分項目中必不可少的存在,如下圖所示。
圖片
- 一般項目使用OSS對象存儲服務(wù),主要是對圖片、文件、音頻等對象集中式管理權(quán)限控制,管理數(shù)據(jù)生命周期等等,提供上傳,下載,預(yù)覽,刪除等功能。
- 通過OSS部署前端項目。
什么是AmazonS3
Amazon Simple Storage Service(Amazon S3,Amazon簡便存儲服務(wù))是 AWS 最早推出的云服務(wù)之一,經(jīng)過多年的發(fā)展,S3 協(xié)議在對象存儲行業(yè)事實(shí)上已經(jīng)成為標(biāo)準(zhǔn)。
- 提供了統(tǒng)一的接口 REST/SOAP 來統(tǒng)一訪問任何數(shù)據(jù)
- 對 S3 來說,存在里面的數(shù)據(jù)就是對象名(鍵),和數(shù)據(jù)(值)
- 不限量,單個文件最高可達(dá) 5TB,可動態(tài)擴(kuò)容。
- 高速。每個 bucket 下每秒可達(dá) 3500 PUT/COPY/POST/DELETE 或 5500 GET/HEAD 請求。
- 具備版本,權(quán)限控制能力
- 具備數(shù)據(jù)生命周期管理能力
作為一個對象存儲服務(wù),S3 功能真的很完備,行業(yè)的標(biāo)桿,目前市面上大部分OSS對象存儲服務(wù)都支持AmazonS3,本文主要講解的就是基于AmazonS3實(shí)現(xiàn)我們自己的 Spring Boot Starter。
阿里云OSS兼容S3
圖片
七牛云對象存儲兼容S3
圖片
騰訊云COS兼容S3
圖片
Minio兼容S3
圖片
我們?yōu)槭裁匆贏mazonS3實(shí)現(xiàn) Spring Boot Starter
原因:市面上OSS對象存儲服務(wù)基本都支持AmazonS3,我們封裝我們的自己的starter那么就必須考慮適配,遷移,可擴(kuò)展。比喻說我們今天使用的是阿里云OSS對接阿里云OSS的SDK,后天我們使用的是騰訊COS對接是騰訊云COS,我們何不直接對接AmazonS3實(shí)現(xiàn)呢,這樣后續(xù)不需要調(diào)整代碼,只需要去各個云服務(wù)商配置就好了。關(guān)注公眾號:碼猿技術(shù)專欄,回復(fù)關(guān)鍵詞:1111 獲取阿里內(nèi)部Java性能調(diào)優(yōu)手冊!
創(chuàng)建一個SpringBoot項目
如下圖所示:創(chuàng)建一個SpringBoot項目。
我們?nèi)∶麨閛ss-spring-boot-starter。
圖片
如下圖所示,創(chuàng)建成功,讓我們進(jìn)入制作的過程吧。
圖片
找到我們需要的依賴
打開maven倉庫,搜索minio
地址:https://mvnrepository.com/
圖片
這里我們選擇第一個,點(diǎn)進(jìn)去后我們選擇1.12.423版本,做演示。
圖片
<!-- 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>
本項目的Pom文件
<?xml versinotallow="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/> <!-- lookup parent from repository -->
</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.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
編寫OssProperties
代碼如下,注釋寫的非常清楚了。@ConfigurationProperties報紅不用管后門會解決。
@Data:注解是lombok,生成get set方法的。
@ConfigurationProperties(prefix = "oss"):將配置文件中oss開頭的屬性綁定到此對象中
大概就是如果想要使用我們的jar他的配置文件有這些配置
oss.endpoint=xxx
oss.accessKey=xxx
oss.secretKey=xxx
/**
* @Author 公眾號:碼猿技術(shù)專欄
* @Description Oss配置類
* @ClassName OssProperties
* @Date 2023/3/18 17:51
**/
@Data
@ConfigurationProperties(prefix = "oss")
public class OssProperties {
/**
* 對象存儲服務(wù)的URL
*/
private String endpoint;
/**
* 區(qū)域
*/
private String region;
/**
* true path-style nginx 反向代理和S3默認(rèn)支持 pathStyle模式 {http://endpoint/bucketname}
* false supports virtual-hosted-style 阿里云等需要配置為 virtual-hosted-style 模式{http://bucketname.endpoint}
* 只是url的顯示不一樣
*/
private Boolean pathStyleAccess = true;
/**
* Access key
*/
private String accessKey;
/**
* Secret key
*/
private String secretKey;
/**
* 最大線程數(shù),默認(rèn):100
*/
private Integer maxConnections = 100;
}
創(chuàng)建一個接口OssTemplate
OssTemplate:oss模板接口,此接口主要是對oss操作的方法的一個接口,定義為接口主要是滿足可擴(kuò)展原則,就是其他人使用了我們的jar包,實(shí)現(xiàn)此接口可以自定義相關(guān)操作。
如下面所示代碼:定義了一些對oss操作的方法。
/**
* @Author 公眾號:碼猿技術(shù)專欄
* @Description oss操作模板
* @ClassName OssTemplate
* @Date 2023/3/18 18:15
**/
public interface OssTemplate {
/**
* 創(chuàng)建bucket
* @param bucketName bucket名稱
*/
void createBucket(String bucketName);
/**
* 獲取所有的bucket
* @return
*/
List<Bucket> getAllBuckets();
/**
* 通過bucket名稱刪除bucket
* @param bucketName
*/
void removeBucket(String bucketName);
/**
* 上傳文件
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @param stream 文件流
* @param contextType 文件類型
* @throws Exception
*/
void putObject(String bucketName, String objectName, InputStream stream, String contextType) throws Exception;
/**
* 上傳文件
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @param stream 文件流
* @throws Exception
*/
void putObject(String bucketName, String objectName, InputStream stream) throws Exception;
/**
* 獲取文件
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @return S3Object
*/
S3Object getObject(String bucketName, String objectName);
/**
* 獲取對象的url
* @param bucketName
* @param objectName
* @param expires
* @return
*/
String getObjectURL(String bucketName, String objectName, Integer expires);
/**
* 通過bucketName和objectName刪除對象
* @param bucketName
* @param objectName
* @throws Exception
*/
void removeObject(String bucketName, String objectName) throws Exception;
/**
* 根據(jù)文件前置查詢文件
* @param bucketName bucket名稱
* @param prefix 前綴
* @param recursive 是否遞歸查詢
* @return S3ObjectSummary 列表
*/
List<S3ObjectSummary> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive);
}
創(chuàng)建OssTemplate的實(shí)現(xiàn)類
如下圖所示:實(shí)現(xiàn)OssTemplate里面的方法,調(diào)用AmazonS3JavaSDK的方法實(shí)現(xiàn)。
AmazonS3提供了眾多的方法,這里就不寫全部的了,公司要用到那些就寫那些吧,后續(xù)擴(kuò)展就行。
AmazonS3接口地址如下
docs.aws.amazon.com/AmazonS3/la…
此類解釋:就是實(shí)現(xiàn)OssTemplate接口,引入AmazonS3客戶端調(diào)用對應(yīng)的接口。
使用的注解解釋:
@RequiredArgsConstructor:lomnok的注解,替代@Autowired。
@SneakyThrows:lomnok的注解,拋出異常。
/**
* @Author 公眾號:碼猿技術(shù)專欄
* @Description OssTemplate的實(shí)現(xiàn)類
* @ClassName OssTemplateImpl
* @Date 2023/3/18 19:02
**/
@RequiredArgsConstructor
public class OssTemplateImpl implements OssTemplate {
private final AmazonS3 amazonS3;
/**
* 創(chuàng)建Bucket
* AmazonS3:https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html
* @param bucketName bucket名稱
*/
@Override
@SneakyThrows
public void createBucket(String bucketName) {
if ( !amazonS3.doesBucketExistV2(bucketName) ) {
amazonS3.createBucket((bucketName));
}
}
/**
* 獲取所有的buckets
* AmazonS3:https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html
* @return
*/
@Override
@SneakyThrows
public List<Bucket> getAllBuckets() {
return amazonS3.listBuckets();
}
/**
* 通過Bucket名稱刪除Bucket
* AmazonS3:https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucket.html
* @param bucketName
*/
@Override
@SneakyThrows
public void removeBucket(String bucketName) {
amazonS3.deleteBucket(bucketName);
}
/**
* 上傳對象
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @param stream 文件流
* @param contextType 文件類型
* AmazonS3:https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html
*/
@Override
@SneakyThrows
public void putObject(String bucketName, String objectName, InputStream stream, String contextType) {
putObject(bucketName, objectName, stream, stream.available(), contextType);
}
/**
* 上傳對象
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @param stream 文件流
* AmazonS3:https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html
*/
@Override
@SneakyThrows
public void putObject(String bucketName, String objectName, InputStream stream) {
putObject(bucketName, objectName, stream, stream.available(), "application/octet-stream");
}
/**
* 通過bucketName和objectName獲取對象
* @param bucketName bucket名稱
* @param objectName 文件名稱
* @return
* AmazonS3:https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html
*/
@Override
@SneakyThrows
public S3Object getObject(String bucketName, String objectName) {
return amazonS3.getObject(bucketName, objectName);
}
/**
* 獲取對象的url
* @param bucketName
* @param objectName
* @param expires
* @return
* AmazonS3:https://docs.aws.amazon.com/AmazonS3/latest/API/API_GeneratePresignedUrl.html
*/
@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();
}
/**
* 通過bucketName和objectName刪除對象
* @param bucketName
* @param objectName
* AmazonS3:https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html
*/
@Override
@SneakyThrows
public void removeObject(String bucketName, String objectName) {
amazonS3.deleteObject(bucketName, objectName);
}
/**
* 根據(jù)bucketName和prefix獲取對象集合
* @param bucketName bucket名稱
* @param prefix 前綴
* @param recursive 是否遞歸查詢
* @return
* AmazonS3:https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html
*/
@Override
@SneakyThrows
public List<S3ObjectSummary> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix);
return objectListing.getObjectSummaries();
}
/**
*
* @param bucketName
* @param objectName
* @param stream
* @param size
* @param contextType
* @return
*/
@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);
}
}
創(chuàng)建OssAutoConfiguration
圖片
OssAutoConfiguration:自動裝配配置類,自動裝配的bean有AmazonS3和OssTemplate
所使用的注解:
@RequiredArgsConstructor:lomnok的注解,替代@Autowired。
@EnableConfigurationProperties(OssProperties.class):自動裝配我們的配置類
@Bean:聲明式bean。
@ConditionalOnMissingBean:修飾bean的一個注解,當(dāng)你的bean被注冊之后,注冊相同類型的bean,就不會成功,它會保證你的bean只有一個,即你的實(shí)例只有一個。多個會報錯。
@ConditionalOnBean(AmazonS3.class):當(dāng)給定的在bean存在時,則實(shí)例化當(dāng)前Bean。
/**
* @Author 公眾號:碼猿技術(shù)專欄
* @Description oss配置bean
* @ClassName OssAConfiguration
* @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());
// url以及region配置
AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(
ossProperties.getEndpoint(), ossProperties.getRegion());
// 憑證配置
AWSCredentials awsCredentials = new BasicAWSCredentials(ossProperties.getAccessKey(),
ossProperties.getSecretKey());
AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
// build amazonS3Client客戶端
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);
}
}
ClientConfiguration對象
客戶端配置,主要是全局的配置信息
看下圖,有很多的配置,有的指定了默認(rèn)值有的沒有,可以到AmazonS3的官方文檔熟悉相關(guān)配置,配置你所需要指定的配置信息等。
只有你真正的理解那些配置的作用才能避免線上的bug。 有興趣的同學(xué)可以看一下。
圖片
創(chuàng)建我們的spring.factories
在resources目錄下新增META-INF包,下面新建spring.factories文件。
這種形式也是"約定大于配置"的體現(xiàn)。讀過spring-boot源碼的同學(xué)應(yīng)該知道,這里就不給大家講解了。
如下圖所示:
org.springframework.boot.autoconfigure.EnableAutoCnotallow=\
com.qing.oss.OssAutoConfiguration
執(zhí)行install打包到我們的本地倉庫
把springboot工程的啟動類,配置文件干掉,干掉Test包。
最重要的是干掉pom文件的spring-boot-maven-plugin,要不然install報錯。
圖片
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
這樣我們的一個oss-spring-boot-starter就完成了。
圖片
執(zhí)行install打包成jar到我們的本地倉庫。
圖片
到我們的本地倉庫就能看到我們的oss-spring-boot-starter
圖片
測試
創(chuàng)建一個spring-boot工程當(dāng)作我們的測試工程
這里就不演示怎么創(chuàng)建項目了。直接看下圖吧
圖片
pom文件新增我們的oss-spring-boot-starter依賴
新增版本全局配置
<properties>
<oss.version>0.0.1-SNAPSHOT</oss.version>
</properties>
新增oss-spring-boot-starter依賴
<dependency>
<groupId>com.qing</groupId>
<artifactId>oss-spring-boot-starter</artifactId>
<version>${oss.version}</version>
</dependency>
刷新maven后可以看到我們依賴加進(jìn)來了。
圖片
解決打包沒有注釋的問題
可以發(fā)現(xiàn)我們的依賴沒有注釋沒有Javadoc注釋。
在我們的oss-string-boot-starter的pom文件下加入下面插件,重新install一下就好了。
<build>
<plugins>
<!-- 在打好的jar包中保留javadoc注釋,實(shí)際會另外生成一個xxxxx-sources.jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
在我們的測試項目里面刷新一下maven可以看到已經(jīng)帶注釋了。
圖片
配置文件添加oss-spring-boot-starter所需要的配置
這里填寫你的阿里云,騰訊cos,七牛云,minio等等的配置。
下面我給大家演示的是Minio
oss.endpoint=xxx
oss.accessKey=xxx
oss.secretKey=xxx
編寫測試方法
如下圖所示,編寫測試方法,執(zhí)行測試方法成功。
@SpringBootTest
class TestOssSpringBpptStarterApplicationTests {
@Autowired
private OssTemplate ossTemplate;
@Test
void contextLoads() {
ossTemplate.createBucket("oss02");
}
}
圖片
到我的Minio中查看發(fā)現(xiàn)測試成功。
圖片
總結(jié)
本文主要講解企業(yè)級OSS對象存儲服務(wù)Spring Boot Starter制作,開箱即用,為項目進(jìn)行賦能。基于AmazonS3協(xié)議,適配市面上的大部分對象存儲服務(wù)如:阿里云OSS、騰訊COS、七牛云OSS、MInio等等。