<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot框架中使用云存儲(chǔ),如何優(yōu)雅地在亞馬遜、華為、阿里、騰...

          共 8554字,需瀏覽 18分鐘

           ·

          2022-04-13 18:46

          Hello,各位小伙伴,好久又沒有更新文章了,不是我不更新,而是產(chǎn)品經(jīng)理太懶了。

          最近產(chǎn)品經(jīng)理又與我扛上了,因?yàn)橄到y(tǒng)使用云環(huán)境不同,產(chǎn)品經(jīng)理提出按環(huán)境變化的一個(gè)需求:

          我們系統(tǒng)有些資料,比如文件、圖片要使用云存儲(chǔ),測(cè)試環(huán)境使用的是亞馬遜云存儲(chǔ);生產(chǎn)環(huán)境要使用華為云存儲(chǔ)。所以系統(tǒng)必須要實(shí)現(xiàn)這2套存儲(chǔ),而且還要按環(huán)境動(dòng)態(tài)切換。如果后面要實(shí)用阿里或者騰訊云,都可以動(dòng)態(tài)切換。讓系統(tǒng)不做太多的改變。

          當(dāng)時(shí)聽到這需求,心想不是很簡(jiǎn)單嘛,只需要下面三步:

          1、定義一個(gè)接口

          2、按不同的云廠商實(shí)現(xiàn)即可

          3、在使用的時(shí)候,根據(jù)使用的廠商名字注入 Service 即可

          可后來(lái)仔細(xì)一想,這樣雖然能完成產(chǎn)品經(jīng)理提出的功能需求,可每次都要修改注入的 Service 代碼,還需要重新打包,實(shí)在很麻煩。作為一名 JAVA 懶漢編程人員,這肯定不是我想要的。

          有沒有更好的方式呢?

          有!

          作為開發(fā)人員一定要干翻產(chǎn)品經(jīng)理,不能丟了開發(fā)人員的顏面,只要你提的出來(lái),我就有對(duì)策。所以在產(chǎn)品經(jīng)理提出需要的時(shí)候,我想也沒想就回答有。

          回答雖然很爽快,但要怎么去實(shí)現(xiàn)呢?

          整理下面的過(guò)程:

          1、定義一個(gè)接口

          2、按廠商實(shí)現(xiàn)接口

          3、根據(jù)廠商注入接口實(shí)現(xiàn)

          So Easy! ?簡(jiǎn)單三步完成,下面一起來(lái)看代碼實(shí)現(xiàn):

          定義接口:

          package com.hx.module.system.v2.service;
          import org.springframework.web.multipart.MultipartFile;
          import java.io.IOException;import java.net.URISyntaxException;
          public interface AmazonS3OperationService {
          /** * 上傳文件到S3服務(wù)器 *

          * key唯一,否則會(huì)變成覆蓋相同key的內(nèi)容 *

          * * @param file / * @param key / * @throws IOException / */ void uploadFile(MultipartFile file, String key) throws IOException;
          /** * 下載文件 * * @param key / */ byte[] downloadFile(String key) throws IOException;
          /** * 刪除文件 * * @param key / */ void deleteFile(String key);
          /** * 獲取文件匿名訪問(wèn)URL * * @param key / * @return / * @throws URISyntaxException / */ String getFileUrl(String key) throws URISyntaxException;}

          我們現(xiàn)在只有亞馬遜、華為云,其實(shí)現(xiàn)如下:

          亞馬遜云實(shí)現(xiàn):

          package com.hx.module.system.v2.service.impl;
          import com.hx.module.system.v2.service.AmazonS3OperationService;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.context.annotation.Configuration;import org.springframework.web.multipart.MultipartFile;import software.amazon.awssdk.core.ResponseInputStream;import software.amazon.awssdk.core.sync.RequestBody;import software.amazon.awssdk.services.s3.S3Client;import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;import software.amazon.awssdk.services.s3.model.GetObjectRequest;import software.amazon.awssdk.services.s3.model.GetObjectResponse;import software.amazon.awssdk.services.s3.model.PutObjectRequest;import software.amazon.awssdk.services.s3.presigner.S3Presigner;import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;import javax.validation.constraints.NotBlank;import java.io.IOException;import java.net.URISyntaxException;import java.time.Duration;import java.util.Objects;
          @Servicepublic class AmazonS3OperationServiceImpl implements AmazonS3OperationService {
          @Value("${amazon-server.bucket}") private String TEST_BUCKET = "hx-aqgl-test";
          @Value("${amazon-server.url-out-time}") private Integer URL_OUT_TIME = 10;
          private final S3Client client;
          private final S3Presigner presigner;
          public AmazonS3OperationServiceImpl(S3Client client, S3Presigner presigner) { this.client = client; this.presigner = presigner; }
          @Override public void uploadFile(MultipartFile file, @NotBlank String key) throws IOException { if (Objects.isNull(file)) { return; } String originalFilename = file.getOriginalFilename(); if (Objects.isNull(originalFilename)) { return; }// int indexOf = originalFilename.lastIndexOf(".");// String suffix = originalFilename.substring(indexOf);// key = key + suffix; PutObjectRequest objectRequest = PutObjectRequest.builder() .bucket(TEST_BUCKET) .key(key) .build(); client.putObject(objectRequest, RequestBody.fromBytes(file.getBytes())); }
          @Override public byte[] downloadFile(@NotBlank String key) throws IOException { GetObjectRequest getObjectRequest = GetObjectRequest.builder() .bucket(TEST_BUCKET) .key(key) .build(); ResponseInputStream object = client.getObject(getObjectRequest); return object.readAllBytes();
          }
          @Override public void deleteFile(@NotBlank String key) { DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder() .bucket(TEST_BUCKET) .key(key) .build(); client.deleteObject(deleteObjectRequest); }
          @Override public String getFileUrl(@NotBlank String key) throws URISyntaxException { GetObjectRequest getObjectRequest = GetObjectRequest.builder() .bucket(TEST_BUCKET) .key(key) .build();
          GetObjectPresignRequest getObjectPresignRequest = GetObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(URL_OUT_TIME)) .getObjectRequest(getObjectRequest) .build();
          PresignedGetObjectRequest presignedGetObjectRequest = presigner.presignGetObject(getObjectPresignRequest); return presignedGetObjectRequest.url().toURI().toString(); }}

          華為云實(shí)現(xiàn):

          package com.hx.module.system.v2.service.impl;
          import com.hx.config.ObsConfig;import com.hx.module.system.utils.UploadFileUtil;import com.hx.module.system.v2.service.AmazonS3OperationService;import com.obs.services.ObsClient;import com.obs.services.model.*;import lombok.extern.slf4j.Slf4j;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.context.annotation.Configuration;import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;import java.io.IOException;import java.io.InputStream;
          @Servicepublic class HuaWeiServerImpl implements AmazonS3OperationService {
          @Resource private ObsConfig obsConfig;
          @Override public void uploadFile(MultipartFile file, String key) throws IOException { PutObjectResult putObjectResult = UploadFileUtil.uploadNetworkStream(obsConfig.createObsClient(), obsConfig.getBucketName(), file.getInputStream(), key); log.info("上傳文件:{}", putObjectResult); }
          @Override public byte[] downloadFile(String key) throws IOException { ObsObject obsObject = obsConfig.createObsClient().getObject(obsConfig.getBucketName(), key); InputStream input = obsObject.getObjectContent(); log.info("下載文件:{}", input.toString()); return input.readAllBytes(); }
          @Override public void deleteFile(String key) { DeleteObjectResult deleteObjectResult = obsConfig.createObsClient().deleteObject( obsConfig.getBucketName(), key); log.info("刪除文件:{}", deleteObjectResult); }
          @Override public String getFileUrl(String key) { TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.PUT, obsConfig.getExpireSeconds() > 60 ? obsConfig.getExpireSeconds() : 300L); request.setBucketName(obsConfig.getBucketName()); request.setObjectKey(key);
          // 創(chuàng)建ObsClient實(shí)例 ObsClient obsClient = obsConfig.createObsClient(); TemporarySignatureResponse response = obsClient.createTemporarySignature(request);
          return response.getSignedUrl(); }}

          不同廠商的云實(shí)現(xiàn)已經(jīng)有了,現(xiàn)在需要在配制文件里添加配制參數(shù):

          ## 配制亞馬遜存儲(chǔ)或者華為云存儲(chǔ)service:  # Amazon 亞馬遜存儲(chǔ)  # huaWei 華為云存儲(chǔ)  type: Amazon

          最后根據(jù)參數(shù)修改亞馬遜云、華為云實(shí)現(xiàn)的注解:

          亞馬遜云實(shí)現(xiàn):?

          // 將 @Service 替換為
          @Configuration@ConditionalOnProperty(name = "service.type", havingValue="Amazon”)

          華為云實(shí)現(xiàn):?

          // 將 @Service 替換為
          @Configuration@ConditionalOnProperty(name = "service.type", havingValue="HuaWei”)

          現(xiàn)在使用的?亞馬遜云 啟動(dòng)系統(tǒng):

          0bfcab6e1aeca132fe6c9c71e3f1a857.webp

          OK,項(xiàng)目啟動(dòng)成功??!

          后面即使使用阿里去,騰訊云或者其它,我只需要pugm實(shí)現(xiàn)一個(gè)接口,在修改配制文件即可啟動(dòng)對(duì)應(yīng)的云Servie

          總結(jié):

          動(dòng)態(tài)切換主要用到了2個(gè)注解:

          1、@Configuration ? 是?Spring-conten 中的一個(gè)注解,將該類聲明為一個(gè)配置類

          2、@ConditionalOnProperty ?是 spring-boot-autoconfigure 中的一個(gè)注解,根據(jù)注解從配制文件中讀取到的值,決定是否把當(dāng)前類注入到 Spring 中

          擴(kuò)展, 以 Conditional 開頭的還有以下注解:

          @ConditionalOnBean:僅在當(dāng)前上下文中存在某個(gè)對(duì)象時(shí),才會(huì)實(shí)例化一個(gè)Bean。

          @ConditionalOnClass:某個(gè)class位于類路徑上,才會(huì)實(shí)例化一個(gè)Bean。

          @ConditionalOnExpression:當(dāng)表達(dá)式值為true的時(shí)候,才會(huì)實(shí)例化一個(gè)Bean。

          @ConditionalOnMissingBean:僅僅在當(dāng)前上下文中不存在某個(gè)對(duì)象時(shí),才會(huì)實(shí)例化一個(gè)Bean。

          @ConditionalOnMissingClass:某個(gè)class類路徑上不存在的時(shí)候,才會(huì)實(shí)例化一個(gè)Bean。

          @ConditionalOnNotWebApplication:非web應(yīng)用,才會(huì)實(shí)例化一個(gè)Bean。

          @ConditionalOnBean:當(dāng)容器中有指定Bean的條件下進(jìn)行實(shí)例化。

          @ConditionalOnMissingBean:當(dāng)容器里沒有指定Bean的條件下進(jìn)行實(shí)例化。

          @ConditionalOnClass:當(dāng)classpath類路徑下有指定類的條件下進(jìn)行實(shí)例化。

          @ConditionalOnMissingClass:當(dāng)類路徑下沒有指定類的條件下進(jìn)行實(shí)例化。

          @ConditionalOnWebApplication:當(dāng)項(xiàng)目是一個(gè)Web項(xiàng)目時(shí)進(jìn)行實(shí)例化。

          @ConditionalOnNotWebApplication:當(dāng)項(xiàng)目不是一個(gè)Web項(xiàng)目時(shí)進(jìn)行實(shí)例化。

          @ConditionalOnProperty:當(dāng)指定的屬性有指定的值時(shí)進(jìn)行實(shí)例化。

          @ConditionalOnExpression:基于SpEL表達(dá)式的條件判斷。

          @ConditionalOnJava:當(dāng)JVM版本為指定的版本范圍時(shí)觸發(fā)實(shí)例化。

          @ConditionalOnResource:當(dāng)類路徑下有指定的資源時(shí)觸發(fā)實(shí)例化。

          @ConditionalOnJndi:在JNDI存在的條件下觸發(fā)實(shí)例化。

          @ConditionalOnSingleCandidate:當(dāng)指定的Bean在容器中只有一個(gè),或者有多個(gè)但是指定了首選的Bean時(shí),才會(huì)觸發(fā)實(shí)例化。

          瀏覽 46
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天日天天躁 | 久久精品无码一区二区三区免费 | 欧美成人在线不卡视频 | 欧美一区 亚洲一区 | 亚洲无码免费高清视频 |