WEB端直傳媒體存儲流程優化實踐
更新時間 2024-10-31 15:40:47
最近更新時間: 2024-10-31 15:40:47
分享文章
介紹WEB端直傳媒體存儲流程優化實踐。
優化實踐
在WEB端需要將用戶的數據上傳到媒體存儲,常見的方法通常是讓用戶通過瀏覽器先上傳文件到用戶的應用服務器,然后應用服務器再上傳到媒體存儲。這種方案上傳的中轉數據需要經過用戶應用服務器,傳輸效率低,同時在多任務上傳的過程,無疑會增加應用服務器的壓力。具體的上傳流程如下圖:

本文將介紹另外一種方案,通過在WEB端直接調用PostObject接口,將用戶的文件對象直傳給天翼云媒體存儲。這種方案,具有傳輸效率高,降低用戶應用服務器壓力等優點。
WEB端直傳媒體存儲流程如下圖:

在WEB端直傳,我們主要基于媒體存儲的post接口,用表單上傳的方式,將對象上傳到指定的桶里面,所以在上傳之前,我們需要先創建桶。同時上傳的對象文件,最大不能超過5GB。
利用post接口進行表單直傳的詳細具體步驟:
1.將post上傳接口中基于表單上傳需要發送的Policy信息,發送給應用服務器。我們假設發送給應用服務器的Policy信息,如下:
{"expiration":"2023-12-28T00:00:00Z","conditions":[{"bucket":"openapi-hp-test"},{"key":"post_dog.png"}]}
2.應用服務器收到Policy信息后,對其進行簽名,然后返回給用戶。
應用服務器,我們可以采用Java,Python,GoLang等語言進行開發,計算post上傳的簽名信息。
我們假設后端應用服務器是python開發的,使用v2簽名,示例代碼如下:
import?base64
import?hmac
import?hashlib
import?binascii
def?sign2(key,?msg):
????return?hmac.new(bytes(key,?encoding='utf-8'),?msg.encode("utf-8"),?hashlib.sha1).digest()
def?getSignatureKey2(key,?encodepolicy):
????signaturebyte?=?sign2(key,?encodepolicy)
????return?binascii.b2a_base64(signaturebyte)
if?__name__?==?"__main__":
????SK?=?'xxxxx'
????bucketName?=?'xx'
????objectKey?=?'xx'
????expirationTime?=?"2023-12-28T00:00:00Z"
????policy?=?"{\"expiration\":?\"%s\","?\
?????????????"\"conditions\":?["?\
?????????????"{\"bucket\":?\"%s\"?},"?\
?????????????"{\"key\":\"%s\"}"?\
?????????????"]}"?%?(expirationTime,?bucketName,?objectKey)
????print(f"policy:{policy}")
????encodePolicy?=?bytes.decode(base64.b64encode(policy.encode('utf-8')))
????print(f"encodePolicy:{encodePolicy}")
?????#計算簽名
????signature?=?getSignatureKey2(SK,encodePolicy)
????print(f"signature:{signature}")
如果使用v4簽名,請參考SDK和Demo相關代碼: SDK概覽
3.準備表單HTML頁面。
表單HTML頁面代碼示例如下:
<html>
<head>
<meta?http-equiv="Content-Type"?content="text/html;?charset=UTF-8"?/>
</head>
<body>
<form?action="//bucket_name.domain.daliqc.cn/"?method="post"?enctype="multipart/form-data">
key
<!--?Object?name?-->
<input?type="text"?name="key"?value="object-key"?/>
<!--?Base64?code?of?the?policy?-->
<input?type="hidden"?name="Policy"?value="***?your?policy?***"?/>
<!--?AK?-->
<input?type="hidden"?name="AWSAccessKeyId"?value="***?your?Access?Key?***"/>
<!--?Signature?information?-->
<input?type="hidden"?name="signature"?value="***?your?signature?***"/>
<input?name="file"?type="file"?/>
<input?name="submit"?value="Upload"?type="submit"?/>
</form>
</body>
</html>
注意:
(1)html表單中的Policy值需要為base64編碼后的值。
(2) html表單中的signature值為你應用服務器的返回的簽名結果。
4.選擇你需要上傳的本地文件,然后進行表單上傳。
常見問題
- 跨域問題:當出現跨域問題的時候,請參考跨域資源共享對其進行配置。
- 參考post接口API相關文檔描述,如果桶為public-read-write,那么Policy可以為空。桶權限非public-read-write時,Policy不能為空,其值在鑒權時需要用到。如果Policy為空,那么AWSAccessKeyId和signature都可以為空,如果Policy不為空,那么就需要同時填入AWSAccessKeyId和signature字段。
- 為避免造成AK/SK泄露,不建議直接在WEB端簽名,可在后端直接計算預簽名URL,然后前端使用預簽名URL授權訪問媒體存儲。
- 這里以應用服務器使用python計算預簽名URL,前端使用臨時URL訪問媒體存儲為例。
- 利用python在應用服務端計算預簽名URL:
import?botocore.config import?botocore.session import?botocore.signers def?generate_putobject_presigned_url(access_key,?secret_key,?end_point,?bucket,key,region): ????config?=?botocore.config.Config(signature_version='s3v4') ????session?=?botocore.session.get_session() ????s3_client?=?session.create_client( ????????'s3', ????????aws_access_key_id=access_key, ????????aws_secret_access_key=secret_key, ????????endpoint_url=end_point, ????????region_name=region, ????????config=config) ????expiration_time?=?3600??#?URL?過期時間(單位:秒) ????#?構建上傳預簽名?URL?的請求參數 ????params?=?{ ????????'Bucket':?bucket, ????????'Key':?key, ????????'ContentType':?'text/plain'??#?替換為你要上傳的文件的?MIME?類型 ????} ????presigned_url?=?s3_client.generate_presigned_url( ????????ClientMethod='put_object', ????????Params=params, ????????ExpiresIn=expiration_time) ????print(f"presigned_url:{presigned_url}") if?__name__?==?'__main__': ????AK?=?'xxx' ????SK?=?'xxx' ????bucketName?=?'hp-test' ????objectKey?=?'post_dog.png' ????endpoint?=?'//domain.daliqc.cn' ????region='ap-east-1' ????generate_putobject_presigned_url(AK,SK,endpoint,bucketName,objectKey,region) - WEB端上傳的時候,URL使用從應用服務器獲取到的預簽名URL:
<html> ??<head> ????<title>使用?PUT?請求上傳文件內容</title> ??</head> ??<body> ????<h1>使用?PUT?請求上傳文件內容</h1> ????<input?type="file"?id="fileInput"?/> ????<button?onclick="uploadFile()">上傳文件</button> ????<script> ??????function?uploadFile()?{ ????????var?fileInput?=?document.getElementById('fileInput'); ????????if?(fileInput.files.length?===?0)?{ ??????????alert('請選擇要上傳的文件'); ??????????return; ????????} ????????var?file?=?fileInput.files[0]; ????????var?xhr?=?new?XMLHttpRequest(); ????????xhr.open('PUT',?'/xxxx',?true);??//?替換成實際的上傳?URL ????????//要上傳的文件的?MIME?類型,需要與生成預簽名的時候一致 ????????xhr.setRequestHeader('Content-Type',?file.type);??//?設置請求頭的?Content-Type ????????xhr.onload?=?function()?{ ??????????if?(xhr.status?===?200)?{ ????????????alert('文件上傳成功'); ??????????}?else?{ ????????????alert('文件上傳失敗'); ??????????} ????????}; ????????xhr.send(file); ??????} ????</script> ??</body> </html>