記一次阿里雲oss文件上傳服務假死

引言

記得以前剛開始學習web項目的時候,經常涉及到需要上傳圖片啥的,那時候都是把圖片上傳到當前項目文件夾下面,每次項目一重啟圖片就丟了。雖然可以通過修改/tomcat/conf/server.xml配置文件,配置一個上傳圖片的本地文件夾,即配置一個工程配置虛擬路徑,這樣可以避免項目重啟圖片丟失。自從參加工作以來基本就沒有遇到使用這種方式來存儲圖片了。一般要麼自己搭建文件服務器,要麼使用付費的文件服務。比如七牛雲、阿里雲、騰訊雲等。今天我們就一起來聊聊如何使用阿里雲OSS文件上傳。

oss 文件上傳

使用OSS文件上傳,阿里雲提供了如下幾種方式,大家可以選擇適合自己的方式。

Web端上傳

Web端常見的上傳方法是用戶在瀏覽器或App端上傳文件到應用服務器,應用服務器再把文件上傳到OSS。具體流程如下圖所示。
在這裡插入圖片描述
這種方式肯定不可取它有如下去缺點:

  • 上傳慢:用戶數據需先上傳到應用服務器,之後再上傳到OSS,網絡傳輸時間比直傳到OSS多一倍。如果用戶數據不通過應用服務器中轉,而是直傳到OSS,速度將大大提升。而且OSS採用BGP帶寬,能保證各地各運營商之間的傳輸速度。
  • 擴展性差:如果後續用戶數量逐漸增加,則應用服務器會成為瓶頸。本來就已經採用了OSS上傳了,然後還要在佔用自己服務器。
  • 費用高:需要準備多台應用服務器。由於OSS上行流量是免費的,如果數據直傳到OSS,將節省多台應用服務器的費用。
JavaScript客戶端簽名直傳

這種方式採用純前端直接上傳,不經過應用服務器,不過這種方式阿里雲給到的一些關於OSS上傳的一些核心參數(AccesssKey ID和AccessKey Secret相當於我們在阿里雲那邊申請的賬號和密碼)也需要寫在前端代碼裏面,這樣就容易導致我們核心參數被泄漏。存在安全隱患。這種方式也不推薦。

服務端簽名後直傳

前面直接在前端簽名上傳會有安全隱患,存在參數泄漏。我們可以把參數放在服務端,然服務端和阿里雲去交互,這樣就不存在核心參數的泄漏。
在這裡插入圖片描述

如何接入

引入依賴
  • 因為本人是從事java開發的,所以直接引入官方提供最新的maven依賴。
<!-- //mvnrepository.com/artifact/com.aliyun.oss/aliyun-sdk-oss -->
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.14.0</version>
</dependency>

為什麼要引入最新的依賴。因為如果遇到什麼問題需要找阿里雲的人幫忙解決的時候,別人大多數都會問你什麼版本的sdk,然後如果遇到那種一時半會比較難解決的問題,人家會推薦你升級最新版本試試。因為可能在最新版本修復了你所遇到的bug。有人可能會說,引入最新版本不就是幫別人踩坑嗎?萬一解決一個bug又引入兩個bug列?這種情況也不是沒有可能的。

服務端構建簽名

在這裡插入圖片描述
上圖是官網提供的入門例子,代碼是一大坨,我們可以看看稍微優化後的代碼:
創建一個單例的ossClient,可以復用線程,不需要每次都去new ossClient().

        String host = String.format("//%s.%s", ossPropertoooies.getBucketName(), ossPropertoooies.getEndpoint());
        long expiredTime = System.currentTimeMillis() + fileOssProperties.getUploadSignatureTtl();
        Date expiration = new Date(expiredTime);

        // 根據文件名和文件類型設置存儲路徑,可以按照文件類型+日期格式+UUID文件名 進行分割
        String filepath = getFilePath(request.getCategory(), request.getFilename());

        PolicyConditions policyConditions = new PolicyConditions();
        policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, fileOssProperties.getUploadSizeLimit());
        policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, filepath);

        String postPolicy = ossClient.generatePostPolicy(expiration, policyConditions);
        byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
        String encodedPolicy = BinaryUtil.toBase64String(binaryData);
        String postSignature = ossClient.calculatePostSignature(postPolicy);

        SignatureDTO signature = new SignatureDTO();
        signature.setAccessId(ossPropertoooies.getAccessKeyId());
        signature.setPolicy(encodedPolicy);
        signature.setSignature(postSignature);
        signature.setFilepath(filepath);
        signature.setHost(host);
        signature.setExpire(fileOssProperties.getUploadSignatureTtl() / 1000);
        signature.setReqFilename(request.getFilename());

接入起來還是非常簡單的,一個後端簽名,前端上傳前後分離的文件上傳就已經完成了。這裡我們使用postman模擬下前端上傳,當然這裡可以改為前端使用ajax,或者其他方式都可以。上傳的url是由我們自己申請的bucketnameendpoint組成的
在這裡插入圖片描述
但是其實這裏面也是有許多坑的我們還是需要稍微注意下。

帶寬限制

上傳和下載都會有帶寬的限制,如果我們是採用外網直傳到阿里雲oss的話,需要注意下我們的外網帶寬是否夠用,以及應對大文件的上傳是不是會把帶寬打滿。如果帶寬被打滿我們上傳就gg了。同樣的下載也有帶寬限制的,需要避免大文件的下載,如果遇到這種大文件下載我們可以採用其他的方式,比如使用oss的客戶端。所以我們需要合理的考慮我們服務器的帶寬。如果我們的應用直接是部署在阿里雲上面的話,我們可以採用內網的上傳和下載。這樣的話就不會有帶寬的限制。

API使用需要注意點

當我們使用OSSclient提供的一些api使用的時候需要仔細去看看裏面是怎麼實現的,或者看看它的文檔有沒有特殊交代的。
比如使用OSSclient提供的processObject方法我們最後需要關閉輸入流,如果流不關閉,鏈接不被釋放。應用鏈接馬上就會被佔滿,然後服務就會成為一個假死的狀態,這個問題我們在生產環境就遇到一次。如下圖所示線程一直沒有被釋放。
在這裡插入圖片描述
像這種為什麼需要我們手動去關閉流,為什麼不直接api幫我們關閉,阿里雲的回復是因為這裡返回的流可能業務方自己需要複製、或者讀什麼的。所以需要調用方主動關閉下,在這個很隱秘的文檔中我們也有找到這個答案。
在這裡插入圖片描述

結束

Tags: