隨著小程序越來越普及,小程序上傳文件必不可少,那么上傳的文件大小就不可控了,小則幾mb,大到好幾百mb,小文件還可以,但是一到超過200mb或稍微再大些的小程序就很容易上傳失敗,導致功能不能繼續進行。以下我們就來解決這個問題,將大文件實現分片上傳
溫馨提示,不要看內容多,其實很好理解,實在看起來頭疼的,就運行起代碼來,逐行打印就明白了,希望能幫到你。代碼在最后,加油!!!
這是一份在前端(uni-app/小程序)直接對接阿里云 OSS 的“分片上傳”實現,采用新版簽名算法 OSS4-HMAC-SHA256(俗稱 SigV4)。
v4簽名官方算法地址:在Header中包含V4簽名(推薦)_對象存儲(OSS)-阿里云幫助中心
服務端獲取sts訪問憑證官方文檔地址:使用STS臨時訪問憑證訪問OSS_對象存儲(OSS)-阿里云幫助中心
開發準備:安裝依賴:npm install crypto-js
流程是:
- 取 STS 臨時憑證;
- 先發起初始化分片(Init)拿到 UploadId;
- 并發讀取本地文件片段,逐片 PUT;
- 最后 POST 完成分片(Complete),把各分片的 ETag 上報,生成最終對象。
關鍵概念與規范
- CanonicalRequest:參與簽名的“規范化請求字符串”,由 6 行組成:Method、CanonicalURI、CanonicalQueryString、CanonicalHeaders、AdditionalHeaders、PayloadHash。
- StringToSign:把 CanonicalRequest 做 SHA256,再與算法名、時間、credentialScope 拼成最終待簽名字符串。
- Authorization:用派生出來的簽名密鑰對 StringToSign 做 HMAC-SHA256,形成簽名后,連同訪問鍵與 AdditionalHeaders 一起寫入該頭。
- UNSIGNED-PAYLOAD:上傳流式/分片時,不對包體求哈希,x-oss-content-sha256 與 PayloadHash 都寫 UNSIGNED-PAYLOAD,以避免前端為大包體計算 sha256 的高開銷。
- 對象元數據 Content-Type 的確定時機:在“初始化分片”時確定,也就是你代碼里把 content-type 設為真實文件類型的原因;后續 PUT/Complete 不會改變它。
代碼分層與每段意義
-
工具層
- toHex、hmacSHA256、sha256HexOfString、sha256HexOfArrayBuffer:簽名/哈希基礎封裝,arrayBufferToWordArray 用來把 ArrayBuffer 轉成 crypto-js 可用的 WordArray。
- utf8ToArrayBuffer:把 XML 等字符串轉為 ArrayBuffer。
- encodeRFC3986/encodePath:路徑/參數嚴格 RFC3986 編碼(OSS4 要求)。
- buildCanonicalQuery:把查詢參數字典規整為“按 key 排序”的 query 串;特別注意空值參數需寫成 ?uploads(無等號)。
- toLowerKeys/trimAndJoinHeaders/signedHeadersListExcluding:規整請求頭(小寫、去多空格、按 key 排序);AdditionalHeaders 是“參與簽名的頭名列表”,此實現特意把 content-type 排除在列表外,但它仍出現在 CanonicalHeaders 內。
-
簽名層
- getSigningKey:按阿里規范派生簽名密鑰:aliyun_v4 + secret → kDate → kRegion → kService(oss) → kSigning('aliyun_v4_request')。
- buildAuthorization:構建 CanonicalRequest、StringToSign、Authorization 字符串,返回三者用于調試/發送。
-
時間
- nowToDateTimeZ:產生 UTC 格式 YYYYMMDDTHHMMSSZ,作為 x-oss-date 與簽名時間。時間漂移過大服務器會拒絕。
-
本地文件讀與 HTTP
- statFile/readFileSlice:小程序文件系統讀文件、按偏移讀取分片。
- requestBuffer:uni.request 的薄封裝。
- asyncPool:并發池,限制同一時間的分片上傳并發數。
-
XML 輔助
- parseXmlTag:解析 OSS 返回的 XML 指定標簽。
- buildCompleteXML:按 PartNumber 升序拼出 CompleteMultipartUpload XML。
- guessContentType:按后綴猜測 Content-Type,用于 Init 請求,確保最終對象是正確媒體類型(mp4/mov/mp3 等)。
主流程 multipartUpload(opts)
入參:filePath、objectKey、partSizeMB、maxConcurrency、onProgress、fetchSts。
- 校驗入參,statFile 獲取總大小。
- await fetchSts() 獲取 STS(AK、SK、Token、bucket、region 等)。
- 計算域名與地區:
- endpointRegion:形如 oss-cn-beijing
- signingRegion:形如 cn-beijing
- 重要的 URI 分離:
- canonicalUriForSign = /${bucket}/${encodePath(objectKey)}(僅用于簽名)
- canonicalUriForReq = /${encodePath(objectKey)}(真實請求路徑)
- 計算對象 MIME:objectContentType = guessContentType(objectKey)。
1) 初始化分片(POST ?uploads)
- 頭:host、x-oss-date、x-oss-security-token、x-oss-content-sha256: UNSIGNED-PAYLOAD、content-type: 對象真實類型。
- 簽名時用 canonicalUriForSign;請求 URL 用 canonicalUriForReq。
- 解析響應的 UploadId。
2) 計算分片列表
- partSize = max(100KB, partSizeMB);生成 partNumbers = [1..N]。
3) 上傳每個分片(PUT ?partNumber=&uploadId= 并發)
- 本地 readFileSlice 讀 ArrayBuffer。
- 頭:content-type: application/octet-stream、x-oss-content-sha256: UNSIGNED-PAYLOAD 等。
- 成功讀取響應頭 ETag,保存 [{PartNumber, ETag}]。
- 進度回調 onProgress(loaded, total)。
4) 完成分片(POST ?uploadId=)
- Body:CompleteMultipartUpload XML(ArrayBuffer)。