管理系統(tǒng)必備技(2):springcloud整合oss文件上傳

微信公眾號:瀟雷
當(dāng)努力到一定程度,幸運(yùn)自與你不期而遇

一、前言

文件上傳在系統(tǒng)中是必須掌握的技能,而云存儲,既能夠方便管理和存儲,在分布式環(huán)境下也很能很好的應(yīng)用,并且有專業(yè)的維護(hù)團(tuán)隊(duì)來維護(hù),可以為企業(yè)節(jié)約開發(fā)成本。

阿里云對象存儲OSS(Object Storage Service)為您提供基于網(wǎng)絡(luò)的數(shù)據(jù)存取服務(wù)。使用OSS,您可以通過網(wǎng)絡(luò)隨時(shí)存儲和調(diào)用包括文本、圖片、音視頻在內(nèi)的各類數(shù)據(jù)文件。這次分享的是如何用阿里云的oss來實(shí)現(xiàn)文件上傳功能。

二、文件上傳方式

對于常見的web文件上傳有這兩種方式:

2.1 文件上傳到自己的應(yīng)用服務(wù)器,提交給網(wǎng)關(guān),然后轉(zhuǎn)發(fā)到自己的應(yīng)用服務(wù),再通過java代碼,將文件上傳到oss,具體的流程圖如下:
image

這種方法有幾個缺點(diǎn):

  • 上傳慢,數(shù)據(jù)先到應(yīng)用服務(wù)器,再去oss,增大了網(wǎng)絡(luò)傳輸
  • 擴(kuò)展性差:如果后續(xù)用戶多了,應(yīng)用服務(wù)器就會成為瓶頸
  • 費(fèi)用高:增加應(yīng)用服務(wù)器本身需要成本
2.2 服務(wù)端簽名后上傳
image

所謂簽名就是先去應(yīng)用服務(wù)器獲得認(rèn)證,通過存在服務(wù)器中的賬號密碼,對用戶發(fā)起的請求做個防偽簽名,簽名中包含著圖片上傳的位置、名稱等信息,但是不包含明碼的accesskey,accessId等信息,然后發(fā)給oss服務(wù)求,oss會對發(fā)送過來的簽名進(jìn)行解析,通過后實(shí)現(xiàn)文件上傳,這種方式就不需要將圖片經(jīng)過應(yīng)用服務(wù)器,而相當(dāng)于去應(yīng)用服務(wù)器中問他能不能上傳,得到反饋后再去上傳的操作。

既保證了安全,又不需要經(jīng)過應(yīng)用服務(wù)器。

三、使用

先去阿里云的oss中創(chuàng)建好自己的bucket,因?yàn)檫@塊內(nèi)容我自己以前創(chuàng)建過了,也比較簡單,就不帶著一起創(chuàng)建了,這就是創(chuàng)建完成的樣子,也需要進(jìn)行accessKey的配置。


image
image
3.1 簡單入門
導(dǎo)依賴:
<dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
        <version>3.5.2</version>
</dependency>
配置信息

這個對象是由endpoint、accessKeyId、accessKeySecret這三個來創(chuàng)建的,完成配置后,即可實(shí)現(xiàn)文件上傳。

// Endpoint以杭州為例,其它Region請按實(shí)際情況填寫。
String endpoint = "oss-cn-shanghai.aliyuncs.com";
// 阿里云主賬號AccessKey擁有所有API的訪問權(quán)限,風(fēng)險(xiǎn)很高。強(qiáng)烈建議您創(chuàng)建并使用RAM賬號進(jìn)行API訪問或日常運(yùn)維,請登錄RAM控制臺創(chuàng)建RAM賬號。
String accessKeyId = "XXX";
String accessKeySecret = "XXX";

// 創(chuàng)建OSSClient實(shí)例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

// 上傳文件。<yourLocalFile>由本地文件路徑加文件名包括后綴組成,例如/users/local/myfile.txt。
ossClient.putObject("xiaolei1996", "system", new File("C:\\Users\\xulei\\Downloads\\u=2311863124,2129220996&fm=26&gp=0 (1).jpg"));

// 關(guān)閉OSSClient。
ossClient.shutdown();
3.2 springcloud Alibaba-oss使用

使用的過程中,alibaba-oss的包更像是一種優(yōu)化,自己不需要在代碼里面創(chuàng)建這個對象,而是在配置文件中引入信息后,通過@Autowired 即可實(shí)現(xiàn)對象注入。

jar包
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>
配置oss
cloud:
  alicloud:
    access-key: xxx
    secret-key: xxx
    oss:
      endpoint: oss-cn-shanghai.aliyuncs.com
測試
@Autowired
OSSClient ossClient;

// 上傳文件。<yourLocalFile>由本地文件路徑加文件名包括后綴組成,例如/users/local/myfile.txt。
ossClient.putObject("xiaolei1996", "system", new File("C:\\Users\\xulei\\Downloads\\u=2311863124,2129220996&fm=26&gp=0 (1).jpg"));
ossClient.shutdown();
public class OSSClient implements OSS {
    private CredentialsProvider credsProvider;
    private URI endpoint;
    private ServiceClient serviceClient;

這塊有個小坑,不要使用OSSClient 來引入這個類,它是個類,而不是接口,因此,需要把注入信息改寫成:

@Autowired
OSSClient ossClient;

不然會有這個錯誤提示:

Description:

Field ossClient in com.xl.gulimall.product.ProductApplication required a bean of type 'com.aliyun.oss.OSSClient' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.aliyun.oss.OSSClient' in your configuration.
3.3 獲得服務(wù)端簽名使用

服務(wù)端簽名后直傳的原理如下:

  • 用戶發(fā)送上傳policy請求到應(yīng)用服務(wù)器
  • 應(yīng)用服務(wù)器返回上傳policy和簽名給用戶
  • 用戶直接上傳數(shù)據(jù)到oss
@Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;
    @Value("${spring.cloud.alicloud.access-key}")
    private String accessKeyId;
    @Value("${spring.cloud.alicloud.secret-key}")
    private String accessKeySecret;

    @RequestMapping("/oss/policy")
    public R policy(){
        String bucket = "xiaolei1996"; // 請?zhí)顚懩?bucketname 。
        String host = "https://" + bucket + "." + endpoint; // host的格式為 bucketname.endpoint
        // callbackUrl為 上傳回調(diào)服務(wù)器的URL,請將下面的IP和Port配置為您自己的真實(shí)信息。
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String dir = format+"/"; // 用戶上傳文件時(shí)指定的前綴。
        Map<String, String> respMap =null;
        // 創(chuàng)建OSSClient實(shí)例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        try {
            long expireTime = 30000;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            // PostObject請求最大可支持的文件大小為5 GB,即CONTENT_LENGTH_RANGE為5*1024*1024*1024。
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);

            respMap= new LinkedHashMap<String, String>();
            respMap.put("accessid", accessKeyId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
            // respMap.put("expire", formatISO8601Date(expiration));
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return R.ok().put("data",respMap);
    }

前端獲得返回的結(jié)果如下:

{
    "msg":"success",
    "code":0,
    "data":{
        "accessid":"LTAIPMS1nt9B7gye",
        "policy":"eyJleHBpcmF0aW9uIjoiMjAyMS0wMS0wMlQxODoxODozOS4wMTZaIiwiY29uZGl0aW9ucyI6W1siY29udGVudC1sZW5ndGgtcmFuZ2UiLDAsMTA0ODU3NjAwMF0sWyJzdGFydHMtd2l0aCIsIiRrZXkiLCIyMDIxLTAxLTAyLyJdXX0=",
        "signature":"yPW7qgQH59UFO4Lx5zwscNsuyNE=",
        "dir":"2021-01-02/",
        "host":"https://xiaolei1996.oss-cn-shanghai.aliyuncs.com",
        "expire":"1609611519"
    }
}
3.4 前端處理返回結(jié)果
<template> 
  <div>
    <el-upload
      action="https://xiaolei1996.oss-cn-shanghai.aliyuncs.com"
      :data="dataObj"
      list-type="picture"
      :multiple="false" :show-file-list="showImage"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :on-success="handleUploadSuccess"
      :on-preview="handlePreview">
      <el-button size="small" type="primary">點(diǎn)擊上傳</el-button>
      <div slot="tip" class="el-upload__tip">只能上傳jpg/png文件,且不超過10MB</div>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="fileList[0].url" alt="">
    </el-dialog>
  </div>
</template>
<script>
   import {policy} from './policy'
   import { getUUID } from '@/utils'

  export default {
    name: 'singleUpload',
    props: {
      value: String
    },
    computed: {
      imageUrl() {
        return this.value;
      },
      imageName() {
        if (this.value != null && this.value !== '') {
          return this.value.substr(this.value.lastIndexOf("/") + 1);
        } else {
          return null;
        }
      },
      fileList () {
        return [{
          name: this.imageName,
          url: this.imageUrl
        }]
      },
      showFileList: {
        get: function () {
          return this.value !== null && this.value !== ''&& this.value!==undefined;
        },
        set: function (newValue) {
        }
      }
    },
    data () {
      return {
        dataObj: {
          policy: '',
          signature: '',
          key: '',
          ossaccessKeyId: '',
          dir: '',
          host: ''
          // callback:'',
        },
        dialogVisible: false,
        showImage: false
      }
    },
    methods: {
      emitInput(val) {
        this.$emit('input', val)
      },
      handleRemove(file, fileList) {
        this.emitInput('');
      },
      handlePreview(file) {
        this.dialogVisible = true;
      },
      beforeUpload(file) {
        let _self = this;
        return new Promise((resolve, reject) => {
          policy().then(response => {
            console.log('響應(yīng)數(shù)據(jù)', response)
            _self.dataObj.policy = response.data.policy
            _self.dataObj.signature = response.data.signature
            _self.dataObj.ossaccessKeyId = response.data.accessid
            _self.dataObj.key = response.data.dir + getUUID() + '_$ {filename} '
            _self.dataObj.dir = response.data.dir
            _self.dataObj.host = response.data.host
            console.log('響應(yīng)數(shù)據(jù)22', _self.dataObj)
            resolve(true)
          }).catch(err => {
            reject(false)
          })
        })
      },
      handleUploadSuccess (res, file) {
        console.log("上傳成功...")
        this.showImage = true
        this.fileList.pop()
        this.fileList.push({ name: file.name, url: this.dataObj.host + '/' + this.dataObj.key.replace(' $ {filename} ', file.name) })
        this.emitInput(this.fileList[0].url)
        console.log('url', this.fileList[0].url)
      }
    }
  }
</script>

上傳結(jié)果展示:


image

四、總結(jié)

這就是一個簡單的oss文件上傳功能,并可以滿足web系統(tǒng)的一個基本開發(fā),如果有額外需要,可以參考o(jì)ss的官方文檔,進(jìn)行更多功能研究。

參考:
阿里云oss

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容