天天看點

這才是企業級的 oss-spring-boot-starter

本文主要講解企業級OSS對象存儲服務Spring Boot Starter制作,開箱即用,為項目進行賦能。基于AmazonS3協定,适配市面上的對象存儲服務如:阿裡雲OSS、騰訊COS、七牛雲OSS、MInio等等。

什麼是OSS?

OSS(Object Storage Service),對象存儲服務,對象存儲服務是一種使用HTTP API存儲和檢索對象的工具。就是将系統所要用的檔案上傳到雲硬碟上,該雲硬碟提供了檔案下載下傳、上傳、預覽等一系列服務,具備版本,權限控制能力,具備資料生命周期管理能力這樣的服務以及技術可以統稱為OSS

OSS在項目中的使用

OSS對象存儲在目前大部分項目中必不可少的存在,如下圖所示。

這才是企業級的 oss-spring-boot-starter
  1. 一般項目使用OSS對象存儲服務,主要是對圖檔、檔案、音頻等對象集中式管理權限控制,管理資料生命周期等等,提供上傳,下載下傳,預覽,删除等功能。
  2. 通過OSS部署前端項目。

什麼是AmazonS3

Amazon Simple Storage Service(Amazon S3,Amazon簡便存儲服務)是 AWS 最早推出的雲服務之一,經過多年的發展,S3 協定在對象存儲行業事實上已經成為标準。

  1. 提供了統一的接口 REST/SOAP 來統一通路任何資料
  2. 對 S3 來說,存在裡面的資料就是對象名(鍵),和資料(值)
  3. 不限量,單個檔案最高可達 5TB,可動态擴容。
  4. 高速。每個 bucket 下每秒可達 3500 PUT/COPY/POST/DELETE 或 5500 GET/HEAD 請求。
  5. 具備版本,權限控制能力
  6. 具備資料生命周期管理能力

作為一個對象存儲服務,S3 功能真的很完備,行業的标杆,目前市面上大部分OSS對象存儲服務都支援AmazonS3,本文主要講解的就是基于AmazonS3實作我們自己的 Spring Boot Starter。

阿裡雲OSS相容S3

這才是企業級的 oss-spring-boot-starter

七牛雲對象存儲相容S3

這才是企業級的 oss-spring-boot-starter

騰訊雲COS相容S3

這才是企業級的 oss-spring-boot-starter

Minio相容S3

這才是企業級的 oss-spring-boot-starter

我們為什麼要基于AmazonS3實作Spring Boot Starter

原因:市面上OSS對象存儲服務基本都支援AmazonS3,我們封裝我們的自己的starter那麼就必須考慮适配,遷移,可擴充。比喻說我們今天使用的是阿裡雲OSS對接阿裡雲OSS的SDK,後天我們使用的是騰訊COS對接是騰訊雲COS,我們何不直接對接AmazonS3實作呢,這樣後續不需要調整代碼,隻需要去各個雲服務商配置就好了。關注公衆号:碼猿技術專欄,回複關鍵詞:1111 擷取阿裡内部Java性能調優手冊!

建立一個SpringBoot項目

如下圖所示:建立一個SpringBoot項目。

我們取名為oss-spring-boot-starter。

這才是企業級的 oss-spring-boot-starter

如下圖所示,建立成功,讓我們進入制作的過程吧。

這才是企業級的 oss-spring-boot-starter

找到我們需要的依賴

打開maven倉庫,搜尋minio

位址:https://mvnrepository.com/

這才是企業級的 oss-spring-boot-starter

這裡我們選擇第一個,點進去後我們選擇1.12.423版本,做示範。

這才是企業級的 oss-spring-boot-starter
<!-- 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 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/> <!-- 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 公衆号:碼猿技術專欄
 * @Description Oss配置類
 * @ClassName OssProperties
 * @Date 2023/3/18 17:51
 **/
@Data
@ConfigurationProperties(prefix = "oss")
public class OssProperties {
    /**
     * 對象存儲服務的URL
     */
    private String endpoint;

    /**
     * 區域
     */
    private String region;

    /**
     * true path-style nginx 反向代理和S3預設支援 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;

    /**
     * 最大線程數,預設:100
     */
    private Integer maxConnections = 100;
}

           

建立一個接口OssTemplate

OssTemplate:oss模闆接口,此接口主要是對oss操作的方法的一個接口,定義為接口主要是滿足可擴充原則,就是其他人使用了我們的jar包,實作此接口可以自定義相關操作。

如下面所示代碼:定義了一些對oss操作的方法。

/**
 * @Author 公衆号:碼猿技術專欄
 * @Description oss操作模闆
 * @ClassName OssTemplate
 * @Date 2023/3/18 18:15
 **/
public interface OssTemplate {

    /**
     * 建立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;

    /**
     * 根據檔案前置查詢檔案
     * @param bucketName bucket名稱
     * @param prefix 字首
     * @param recursive 是否遞歸查詢
     * @return S3ObjectSummary 清單
     */
    List<S3ObjectSummary> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive);
}

           

建立OssTemplate的實作類

如下圖所示:實作OssTemplate裡面的方法,調用AmazonS3JavaSDK的方法實作。

AmazonS3提供了衆多的方法,這裡就不寫全部的了,公司要用到那些就寫那些吧,後續擴充就行。

AmazonS3接口位址如下

docs.aws.amazon.com/AmazonS3/la…

此類解釋:就是實作OssTemplate接口,引入AmazonS3用戶端調用對應的接口。

使用的注解解釋:

@RequiredArgsConstructor:lomnok的注解,替代@Autowired。

@SneakyThrows:lomnok的注解,抛出異常。

/**
 * @Author 公衆号:碼猿技術專欄
 * @Description OssTemplate的實作類
 * @ClassName OssTemplateImpl
 * @Date 2023/3/18 19:02
 **/
@RequiredArgsConstructor
public class OssTemplateImpl implements OssTemplate {

    private final AmazonS3 amazonS3;

    /**
     * 建立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);
    }

    /**
     * 根據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);

    }
}

           

建立OssAutoConfiguration

這才是企業級的 oss-spring-boot-starter

OssAutoConfiguration:自動裝配配置類,自動裝配的bean有AmazonS3和OssTemplate

所使用的注解:

@RequiredArgsConstructor:lomnok的注解,替代@Autowired。

@EnableConfigurationProperties(OssProperties.class):自動裝配我們的配置類

@Bean:聲明式bean。

@ConditionalOnMissingBean:修飾bean的一個注解,當你的bean被注冊之後,注冊相同類型的bean,就不會成功,它會保證你的bean隻有一個,即你的執行個體隻有一個。多個會報錯。

@ConditionalOnBean(AmazonS3.class):當給定的在bean存在時,則執行個體化目前Bean。

/**
 * @Author 公衆号:碼猿技術專欄
 * @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對象

用戶端配置,主要是全局的配置資訊

看下圖,有很多的配置,有的指定了預設值有的沒有,可以到AmazonS3的官方文檔熟悉相關配置,配置你所需要指定的配置資訊等。

隻有你真正的了解那些配置的作用才能避免線上的bug。 有興趣的同學可以看一下。

這才是企業級的 oss-spring-boot-starter

建立我們的spring.factories

在resources目錄下新增META-INF包,下面建立spring.factories檔案。

這種形式也是"約定大于配置"的展現。讀過spring-boot源碼的同學應該知道,這裡就不給大家講解了。

如下圖所示:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.qing.oss.OssAutoConfiguration
           

執行install打包到我們的本地倉庫

把springboot工程的啟動類,配置檔案幹掉,幹掉Test包。

最重要的是幹掉pom檔案的spring-boot-maven-plugin,要不然install報錯。

這才是企業級的 oss-spring-boot-starter
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
           

這樣我們的一個oss-spring-boot-starter就完成了。

這才是企業級的 oss-spring-boot-starter

執行install打包成jar到我們的本地倉庫。

這才是企業級的 oss-spring-boot-starter

到我們的本地倉庫就能看到我們的oss-spring-boot-starter

這才是企業級的 oss-spring-boot-starter

測試

建立一個spring-boot工程當作我們的測試工程

這裡就不示範怎麼建立項目了。直接看下圖吧

這才是企業級的 oss-spring-boot-starter

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後可以看到我們依賴加進來了。

這才是企業級的 oss-spring-boot-starter

解決打包沒有注釋的問題

可以發現我們的依賴沒有注釋沒有Javadoc注釋。

在我們的oss-string-boot-starter的pom檔案下加入下面插件,重新install一下就好了。

<build>
        <plugins>
            <!-- 在打好的jar包中保留javadoc注釋,實際會另外生成一個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可以看到已經帶注釋了。

這才是企業級的 oss-spring-boot-starter

配置檔案添加oss-spring-boot-starter所需要的配置

這裡填寫你的阿裡雲,騰訊cos,七牛雲,minio等等的配置。

下面我給大家示範的是Minio

oss.endpoint=xxx
oss.accessKey=xxx
oss.secretKey=xxx
           

編寫測試方法

如下圖所示,編寫測試方法,執行測試方法成功。

@SpringBootTest
class TestOssSpringBpptStarterApplicationTests {
    @Autowired
    private OssTemplate ossTemplate;

    @Test
    void contextLoads() {
        ossTemplate.createBucket("oss02");
    }
}
           
這才是企業級的 oss-spring-boot-starter

到我的Minio中檢視發現測試成功。

這才是企業級的 oss-spring-boot-starter

來源:https://juejin.cn/post/7211828279430021180

總結

本文主要講解企業級OSS對象存儲服務Spring Boot Starter制作,開箱即用,為項目進行賦能。基于AmazonS3協定,适配市面上的大部分對象存儲服務如:阿裡雲OSS、騰訊COS、七牛雲OSS、MInio等等

最後說一句(别白嫖,求關注)

陳某每一篇文章都是精心輸出,如果這篇文章對你有所幫助,或者有所啟發的話,幫忙點贊、關注、轉發、,你的支援就是我堅持下去的最大動力!