微信小程序直傳實(shí)踐
本文介紹如何在微信小程序環(huán)境下將文件上傳到OSS。
背景信息
小程序是當(dāng)下比較流行的移動(dòng)應(yīng)用,例如大家熟知的微信小程序、支付寶小程序等。小程序是一種全新的開發(fā)模式,無需下載和安裝,為終端用戶提供更優(yōu)的用戶體驗(yàn)。如何在小程序環(huán)境下上傳文件到OSS也成為開發(fā)者比較關(guān)心的一個(gè)問題。
與JavaScript客戶端直傳實(shí)踐的原理相同,小程序上傳文件到OSS也是利用OSS提供的PostObject接口來實(shí)現(xiàn)表單文件上傳到OSS。關(guān)于PostObject的詳細(xì)介紹,請(qǐng)參見PostObject。
步驟1:微信小程序配置域名白名單
您可以為微信小程序配置域名白名單,以實(shí)現(xiàn)微信小程序和OSS Bucket之間的正常通信。
單擊Bucket 列表,然后單擊目標(biāo)Bucket名稱。
在Bucket內(nèi)的左側(cè)導(dǎo)航欄,單擊概覽,然后在訪問端口區(qū)域查看Bucket域名。
登錄微信小程序平臺(tái),將上傳和下載的合法域名填寫為Bucket的外網(wǎng)訪問域名。
說明實(shí)際業(yè)務(wù)中,建議您將OSS提供的外網(wǎng)域名和您自己的域名進(jìn)行綁定,以便使用自定義域名訪問OSS存儲(chǔ)空間中的文件。配置步驟,請(qǐng)參見綁定自定義域名。
步驟2:獲取簽名
為了您的數(shù)據(jù)安全,建議使用簽名方式上傳文件。OSS提供服務(wù)端簽名和客戶端簽名兩種簽名方式。
使用服務(wù)端簽名時(shí),您需要先搭建一個(gè)簽名服務(wù),然后由客戶端調(diào)用簽名服務(wù)生成簽名。
服務(wù)端簽名源碼
uploadOssHelper.js代碼如下:
說明使用代碼前,您需要先安裝Node.js SDK。更多信息,請(qǐng)參見安裝。
const crypto = require("crypto-js"); class MpUploadOssHelper { constructor(options) { this.accessKeyId = options.accessKeyId; this.accessKeySecret = options.accessKeySecret; // 限制參數(shù)的生效時(shí)間,單位為小時(shí),默認(rèn)值為1。 this.timeout = options.timeout || 1; // 限制上傳文件的大小,單位為MB,默認(rèn)值為10。 this.maxSize = options.maxSize || 10; } createUploadParams() { const policy = this.getPolicyBase64(); const signature = this.signature(policy); return { OSSAccessKeyId: this.accessKeyId, policy: policy, signature: signature, }; } getPolicyBase64() { let date = new Date(); // 設(shè)置policy過期時(shí)間。 date.setHours(date.getHours() + this.timeout); let srcT = date.toISOString(); const policyText = { expiration: srcT, conditions: [ // 限制上傳文件大小。 ["content-length-range", 0, this.maxSize * 1024 * 1024], ], }; const buffer = Buffer.from(JSON.stringify(policyText)); return buffer.toString("base64"); } signature(policy) { return crypto.enc.Base64.stringify( crypto.HmacSHA1(policy, this.accessKeySecret) ); } } module.exports = MpUploadOssHelper;
服務(wù)端接口示例
以Express為例,接口代碼如下:
const express = require("express"); const app = express(); const MpUploadOssHelper = require("./uploadOssHelper.js"); app.get("/getPostObjectParams", (req, res) => { const mpHelper = new MpUploadOssHelper({ // 從環(huán)境變量中獲取訪問憑證。運(yùn)行本代碼示例之前,請(qǐng)確保已設(shè)置環(huán)境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 accessKeyId: process.env.OSS_ACCESS_KEY_ID, accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET, // 限制參數(shù)的生效時(shí)間,單位為小時(shí),默認(rèn)值為1。 timeout: 1, // 限制上傳文件大小,單位為MB,默認(rèn)值為10。 maxSize: 10, }); // 生成參數(shù)。 const params = mpHelper.createUploadParams(); res.json(params); });
使用客戶端簽名時(shí),您需要先在服務(wù)端搭建一個(gè)STS服務(wù),然后由客戶端獲取STS臨時(shí)授權(quán)賬號(hào)并生成簽名。
服務(wù)端搭建STS服務(wù)
const STS = require("ali-oss").STS; const express = require("express"); const app = express(); const stsClient = new STS({ // 從環(huán)境變量中獲取訪問憑證。運(yùn)行本代碼示例之前,請(qǐng)確保已設(shè)置環(huán)境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 accessKeyId: process.env.OSS_ACCESS_KEY_ID, accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET, // 填寫B(tài)ucket名稱。 bucket: "YourBucketName", }); async function getToken() { // 指定角色的ARN,格式為acs:ram::$accountID:role/$roleName。 const STS_ROLE = "yourStsRole"; const STSpolicy = { Statement: [ { Action: ["oss:*"], Effect: "Allow", Resource: ["acs:oss:*:*:*"], }, ], Version: "1", }; const result = await stsClient.assumeRole( STS_ROLE, STSpolicy, 3600 // STS過期時(shí)間,單位為秒。 ); const { credentials } = result; return credentials; } app.get("/getToken", async (req, res) => { // 獲取STS。 const credentials = await getToken(); console.log(credentials.AccessKeyId); console.log(credentials.AccessKeySecret); console.log(credentials.SecurityToken); res.json(credentials); });
說明STSpolicy
包含版本號(hào)(Version)和授權(quán)語句(Statement)。每條授權(quán)語句又包含授權(quán)效力(Effect)、操作(Action)、資源(Resource)。更多信息,請(qǐng)參見RAM Policy。客戶端獲取STS臨時(shí)訪問憑證并生成簽名
import crypto from 'crypto-js'; import { Base64 } from 'js-base64'; // 計(jì)算簽名。 function computeSignature(accessKeySecret, canonicalString) { return crypto.enc.Base64.stringify(crypto.HmacSHA1(canonicalString, accessKeySecret)); } const date = new Date(); date.setHours(date.getHours() + 1); const policyText = { expiration: date.toISOString(), // 設(shè)置policy過期時(shí)間。 conditions: [ // 限制上傳大小。 ["content-length-range", 0, 1024 * 1024 * 1024], ], }; async function getFormDataParams() { const credentials = await axios.get('/getToken') const policy = Base64.encode(JSON.stringify(policyText)) // policy必須為base64的string。 const signature = computeSignature(credentials.AccessKeySecret, policy) const formData = { OSSAccessKeyId: credentials.AccessKeyId, signature, policy, 'x-oss-security-token': credentials.SecurityToken } return formData }
步驟3:使用微信小程序上傳
使用uploadFile接口上傳文件,示例代碼如下:
const host = '<host>';
const signature = '<signatureString>';
const ossAccessKeyId = '<accessKey>';
const policy = '<policyBase64Str>';
const key = '<object name>';
const securityToken = '<x-oss-security-token>';
const filePath = '<filePath>'; // 待上傳文件的文件路徑。
wx.uploadFile({
url: host,
filePath: filePath,
name: 'file', // 必須填file。
formData: {
key,
policy,
OSSAccessKeyId: ossAccessKeyId,
signature,
// 'x-oss-security-token': securityToken // 使用STS簽名時(shí)必傳。
},
success: (res) => {
if (res.statusCode === 204) {
console.log('上傳成功');
}
},
fail: err => {
console.log(err);
}
});
參數(shù) | 是否必選 | 說明 |
host | 是 | 填寫訪問域名,且必須攜帶https。支持以下兩種類型的訪問域名:
|
signature | 是 | 填寫步驟2中獲取到的signature信息。 |
ossAccessKeyId | 是 | 填寫您的AccessKey ID。如果您是通過STS獲取的臨時(shí)用戶,則填寫臨時(shí)用戶的AccessKey ID。 |
policy | 是 | 填寫步驟2中獲取到的policy信息。 |
key | 是 | 設(shè)置文件上傳至OSS后的文件路徑。例如,您需要將myphoto.jpg上傳至test文件夾下,則填寫 |
securityToken | 是 | 如果使用STS認(rèn)證,則填寫步驟2中使用客戶端簽名獲取到的SecurityToken。 |
filePath | 是 | 填寫待上傳文件的本地完整路徑,例如 |
常見問題
iOS系統(tǒng)使用微信小程序直傳后,返回的結(jié)果中,為什么ETag參數(shù)名稱會(huì)變?yōu)镋tag?
問題原因:使用微信小程序直傳后,OSS會(huì)返回標(biāo)準(zhǔn)的ETag。但是iOS系統(tǒng)的微信小程序會(huì)將返回的ETag轉(zhuǎn)成了Etag,導(dǎo)致客戶端調(diào)用ETag參數(shù)的業(yè)務(wù)出現(xiàn)問題。
解決方案:若您的業(yè)務(wù)涉及多個(gè)系統(tǒng)的微信小程序直傳場景,建議您將Header中的所有參數(shù)名都轉(zhuǎn)成小寫進(jìn)行兼容。
上傳成功后,如何獲取文件URL?
具體操作,請(qǐng)參見使用文件URL分享文件
PostObject請(qǐng)求時(shí),報(bào)錯(cuò)The bucket POST must contain the specified 'key'. If it is specified, please check the order of the fields.怎么辦?
問題原因:沒有指定key表單域。
解決方案:PostObject請(qǐng)求時(shí)必須指定key表單域。更多信息,請(qǐng)參見PostObject。
微信小程序是否支持同時(shí)上傳多個(gè)文件?
不支持,單次調(diào)用PostObject接口僅支持上傳一個(gè)文件。如果您希望通過微信小程序上傳多個(gè)文件,請(qǐng)循環(huán)調(diào)用PostObject接口。
OSS SDK是否支持微信小程序直傳?
不支持。微信小程序直傳基于微信小程序API實(shí)現(xiàn),與OSS SDK無關(guān)。
微信小程序是否支持?jǐn)帱c(diǎn)續(xù)傳?
OSS基于微信的uploadFile接口上傳文件,該接口不支持?jǐn)帱c(diǎn)續(xù)傳。
微信小程序是否支持刪除OSS資源?
微信小程序支持刪除OSS資源,但是需要您自行實(shí)現(xiàn)簽名邏輯。