前言
通常情況下,作為前后端分離的項目來說,文件上傳是最尋常的功能之一。雖然每個公司選擇的文件管理云庫各不相同,但實現思路基本一致。我所在公司使用阿里云oss文件管理,之前服務端做了透傳,但是由于每個測試環境的帶寬限制,導致在測試環境出現文件上傳受限的問題。因此,痛定思痛,決定拋開中間層,直傳阿里云OSS。
上干貨
根據終端不同,阿里云oss官方文檔提供了多種直傳方式。
1、web端直傳
2、移動應用端直傳
3、小程序直傳
4、鴻蒙直傳
由于react-native是跨平臺框架,又是通過js編寫的,因此可供選擇的有web端直傳和移動應用端直傳。但是對于很多RN開發者并不具備很強的原生開發能力,而且還需要引入OSS Android SDK,OSS iOS SDK,如果同時開發鴻蒙的話還需要引入OSS?Harmony?SDK,這樣不但維護成本高,開發周期也會增加,況且不利于維護。因此web端直傳成為唯一可供選擇,并且是最具完美的直傳方式。
根據官網實踐例子,一步一步進行。
服務端配置
您可以使用PostObject接口,將文件直接從 Web 端上傳到 OSS,服務器生成的簽名為直傳操作提供安全保障,同時支持配置上傳策略(Policy)以限制上傳操作并滿足業務需求。
實現服務端簽名直傳,只需3步:
說明?由于使用了臨時訪問憑證,整個過程中不會泄露業務服務器的長期密鑰,保證了文件上傳的安全性。
-
配置OSS:在控制臺創建一個Bucket,用于存儲用戶上傳的文件。同時,為 Bucket 配置跨域資源共享(CORS) 規則,以允許來自 Web 端的跨域請求。
-
配置服務端:調用STS服務獲取一個臨時訪問憑證,然后使用臨時訪問憑證和服務端預設的上傳策略Policy(如Bucket名稱、目錄路徑、過期時間等)生成簽名授權用戶在一定時間內進行文件上傳。
{"Version": "1","Statement": [{"Effect": "Allow","Action": "oss:PutObject","Resource": "acs:oss:*:*:<Bucket名稱>/*"}] }
?
-
配置Web端:構造HTML表單請求,通過表單提交使用簽名將文件上傳到OSS。
前端上傳接口所需的oss配置參數,后端需要提供業務接口來提供,接收到所有必需的配置信息后,就可以請求上傳了。此請求將直接與OSS服務進行通信,從而實現文件的上傳。以下字段需要后端通過接口提供,至于后端如何獲取,請參考服務端簽名及參數組裝
客戶端實現
?根據官網給的web端例子,在ReactNative下是否可以直接使用呢?答案是否定的!
web端例子
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>服務端生成簽名上傳文件到OSS</title>
</head>
<body>
<div class="container"><form><div class="mb-3"><label for="file" class="form-label">選擇文件:</label><input type="file" class="form-control" id="file" name="file" required /></div><button type="submit" class="btn btn-primary">上傳</button></form>
</div><script type="text/javascript">
document.addEventListener('DOMContentLoaded', function () {const form = document.querySelector("form");const fileInput = document.querySelector("#file");form.addEventListener("submit", (event) => {event.preventDefault();const file = fileInput.files[0];if (!file) {alert('請選擇一個文件再上傳。');return;}const filename = file.name;fetch("/get_post_signature_for_oss_upload", { method: "GET" }).then((response) => {if (!response.ok) {throw new Error("獲取簽名失敗");}return response.json();}).then((data) => {let formData = new FormData();formData.append("success_action_status", "200");formData.append("policy", data.policy);formData.append("x-oss-signature", data.signature);formData.append("x-oss-signature-version", "OSS4-HMAC-SHA256");formData.append("x-oss-credential", data.x_oss_credential);formData.append("x-oss-date", data.x_oss_date);formData.append("key", data.dir + file.name); // 文件名formData.append("x-oss-security-token", data.security_token);formData.append("file", file); // file 必須為最后一個表單域return fetch(data.host, { method: "POST",body: formData});}).then((response) => {if (response.ok) {console.log("上傳成功");alert("文件已上傳");} else {console.log("上傳失敗", response);alert("上傳失敗,請稍后再試");}}).catch((error) => {console.error("發生錯誤:", error);});});
});
</script>
</body>
</html>
react-native中實現
// 多媒體類型
export const nameType = (type: string) => {switch (type) {case 'image':return 'image.png';case 'video':return `video.mp4`;case 'voice':return 'voice.wav';default:return 'image.png';}
};export const uploadFileToOSS = async function (file: string,type: string,config?: AxiosRequestConfig) {const ossConfig = await getOssConfig(); // 后端接口提供oss配置const fileName =`${ossConfig.dir}${Date.now()}_${nameType(type)}`const formData = new FormData();formData.append('success_action_status', '200');formData.append('policy', ossConfig.policy);formData.append("x-oss-signature", ossConfig.signature);formData.append("x-oss-signature-version", ossConfig.version);formData.append("x-oss-credential", ossConfig.x_oss_credential);formData.append("x-oss-date", ossConfig.x_oss_date);formData.append('key', fileName );formData.append("x-oss-security-token", ossConfig.security_token);formData.append('file', {uri: file, name: nameType(type), type: 'multipart/form-data'});try {const response = await axios({method: 'POST',url: ossConfig.host,data: formData,headers: { 'Content-Type': 'multipart/form-data' }});if (response.status == 200) {console.log(ossConfig.host+fileName )} else {throw new Error(`上傳失敗: ${response.status}`);}} catch (error) {if (!error.response) {console.error('Network Error:', error); // 如 Network Error} else {console.error('Response Error:', error.response.status, error.response.statusText, error.response);}}
}
看著似乎沒什么問題,那么去運行,果不其然,報錯了,而且沒有太多有用的信息。起初以為是參數傳錯了,經過仔細檢查一點沒錯。
這就怪了,經過反復的刪減修改,懷疑是文件格式問題,當然事實也確實如此。
修改文件格式后,終于,終于看到了黎明的曙光,100M的視頻瞬間上傳成功后,那種喜悅涌上心頭,那種手足無措后的驚喜,頓時讓我忘卻了幾天來焦頭爛額的疲憊。
好了下班了,幾天沒睡個好覺了,先休息了,下一篇我將詳細闡述如何解決文件格式導致ReactNative中使用web端直傳OSS報錯的問題。
歡迎猿友們評論,提問。如果覺得寫的不錯點個贊再走哦!
?