vue实现小程序oss分片上传
随着小程序越来越普及,小程序上传文件必不可少,那么上传的文件大小就不可控了,小则几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)。