Closed sonofmagic closed 1 year ago
同遇到问题,真的就他最大了
真的离谱,就属他最大
O(∩_∩)O哈哈~,是的,这个 issue 也提了 1 个月多了也没人理,好蛋疼。
O(∩_∩)O哈哈~,是的,这个 issue 也提了 1 个月多了也没人理,好蛋疼。
我自己写了一个,非常简单的。不用他这个了 可以参考这个来实现 https://www.tencentcloud.com/zh/document/product/436/30934
@sonofmagic
import { getFileUploadTempKey } from "@/services/api";
import Taro from "@tarojs/taro";
import CosAuth from "./cos-auth.min.js"; // 这里引用了 cos-auth.js,下载地址为 https://unpkg.com/cos-js-sdk-v5/demo/common/cos-auth.min.js
// 是否使用后缀式,涉及签名计算和域名白名单配置,后缀式说明看上文
const FORCE_PATH_STYLE = false;
export interface ICosUploadOptions {
/**
* 小程序文件路径
*/
filePath: string;
/**
* 上传目录路径
*/
fileKey?: string;
}
/**
* 上传到cos存储桶
* 相关文档 https://www.tencentcloud.com/zh/document/product/436/30934
* @param options
* @param onProgres
* @returns
*/
export function uploadFile(options: ICosUploadOptions, onProgres?: Function) {
const filePath = options.filePath;
const fileKey = options.fileKey || "mall-img/";
// 对更多字符编码的 url encode 格式
const camSafeUrlEncode = function (str) {
return encodeURIComponent(str)
.replace(/!/g, "%21")
.replace(/'/g, "%27")
.replace(/\(/g, "%28")
.replace(/\)/g, "%29")
.replace(/\*/g, "%2A");
};
const getAuthorization = () => {
return new Promise<any>(async (resolve, reject) => {
try {
// 此处为后端接口请求获取签名
const { info }: any = await getFileUploadTempKey();
const signPathname = FORCE_PATH_STYLE ? "/" + info.bucket + "/" : "/";
const auth = {
bucket: info.bucket,
region: info.region,
XCosSecurityToken: info.credentials.sessionToken,
Authorization: CosAuth({
SecretId: info.credentials.tmpSecretId,
SecretKey: info.credentials.tmpSecretKey,
Method: "POST",
Pathname: signPathname,
}),
};
resolve(auth);
} catch (error) {
reject(error);
}
});
};
return new Promise<any>(async (resolve, reject) => {
const filename = `${fileKey}${filePath.substr(
filePath.lastIndexOf("/") + 1
)}`; // 这里指定上传的文件名
const AuthData = await getAuthorization();
// 前缀式
const prefix = `https://${AuthData.bucket}.cos.${AuthData.region}.myqcloud.com/`;
// 后缀式
const suffix = `https://cos.${AuthData.region}.myqcloud.com/${AuthData.bucket}/`;
let uploadUrl = prefix;
if (FORCE_PATH_STYLE) {
uploadUrl = suffix;
}
const requestTask = Taro.uploadFile({
url: uploadUrl,
name: "file",
filePath: filePath,
formData: {
key: filename,
success_action_status: 200,
Signature: AuthData.Authorization,
"x-cos-security-token": AuthData.XCosSecurityToken,
"Content-Type": "",
},
success: function (res) {
const url = prefix + camSafeUrlEncode(filename).replace(/%2F/g, "/");
if (/^2\d\d$/.test("" + res.statusCode)) {
resolve(url);
} else {
reject(res);
}
},
fail: function (res) {
reject(res);
},
});
requestTask.onProgressUpdate(function (res) {
onProgres && onProgres(res);
});
});
}
// 使用
// 选择文件
Taro.chooseImage({
count: 1, // 默认9
sizeType: ['original'], // 可以指定是原图还是压缩图,这里默认用原图
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
uploadFile({
filePath: res.tempFiles[0].path
}, (progres) => {
// 上传进度
console.log("🚀 ~ file: index.ts:111 ~ progres:", progres)
}).then((data) => {
// 直出url
console.log("🚀 ~ file: index.ts:111 ~ data:", data)
});
}
})
感谢大佬分享,@carsonxu @livehigh 参考一下大佬的思路,优化一下这个 sdk
感谢大佬分享,@carsonxu @livehigh 参考一下大佬的思路,优化一下这个 sdk
收到大家的反馈,小程序sdk因为提供了多种api以及内置了部分api调用需要的依赖库,导致整体文件偏大。后续我们会针对这些依赖库寻找更小体积的替代品做优化。 当您的业务仅仅需要上传功能且不需要使用分块上传时,可以不使用sdk,用简单上传的方式即可。 https://github.com/tencentyun/cos-wx-sdk-v5/issues/85
esm 版 cos-auth.js
import CryptoJS from 'crypto-js'
// 和 cam 保持一致的 url encode
function camSafeUrlEncode(str) {
return encodeURIComponent(str)
.replace(/!/g, '%21')
.replace(/'/g, '%27')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
.replace(/\*/g, '%2A')
}
const HmacSHA1 = function (text, key) {
return CryptoJS.HmacSHA1(text, key).toString()
}
const SHA1 = function (text) {
return CryptoJS.SHA1(text).toString()
}
const base64 = function (text) {
return CryptoJS.enc.Utf8.parse(text).toString(CryptoJS.enc.Base64)
}
// v4 签名
const CosAuthV4 = function (opt) {
let pathname = opt.Pathname || '/'
let expires = opt.Expires
let ShortBucketName = ''
let AppId = ''
let match = opt.Bucket.match(/^(.+)-(\d+)$/)
if (match) {
ShortBucketName = match[1]
AppId = match[2]
}
let random = parseInt(Math.random() * 2 ** 32)
let now = parseInt(Date.now() / 1000)
let e = now + (expires === undefined ? 900 : (expires * 1 || 0)) // 默认签名过期时间为当前时间 + 900s
let path = `/${AppId}/${ShortBucketName}${encodeURIComponent(pathname).replace(/%2F/g, '/')}` //多次签名这里填空
let plainText = `a=${AppId}&b=${ShortBucketName}&k=${opt.SecretId}&e=${e}&t=${now}&r=${random}&f=${path}`
let sha1Res = CryptoJS.HmacSHA1(plainText, opt.SecretKey)
let strWordArray = CryptoJS.enc.Utf8.parse(plainText)
let resWordArray = sha1Res.concat(strWordArray)
let sign = resWordArray.toString(CryptoJS.enc.Base64)
console.log('sign:', sign)
let res = HmacSHA1(plainText, opt.SecretKey)
console.log('res:', res)
console.log('sign1:', base64(res))
console.log('sign2:', base64(plainText))
console.log('sign2:', base64(res + plainText))
return sign
}
// PostObject policy 签名
const CosAuthPolicy = function (opt) {
let now = Math.round(Date.now() / 1000)
let exp = now + (opt.Expires || 900)
let qKeyTime = `${now};${exp}`
let qSignAlgorithm = 'sha1'
let policy = JSON.stringify({
'expiration': new Date(exp * 1000).toISOString(),
'conditions': [
// {'acl': query.ACL},
// ['starts-with', '$Content-Type', 'image/'],
// ['starts-with', '$success_action_redirect', redirectUrl],
// ['eq', '$x-cos-server-side-encryption', 'AES256'],
{ 'q-sign-algorithm': qSignAlgorithm },
{ 'q-ak': opt.SecretId },
{ 'q-sign-time': qKeyTime },
{ 'bucket': opt.Bucket },
{ 'key': opt.Key },
],
})
// 签名算法说明文档:https://www.qcloud.com/document/product/436/7778
// 步骤一:生成 SignKey
let signKey = HmacSHA1(qKeyTime, opt.SecretKey)
// 步骤二:生成 StringToSign
let stringToSign = SHA1(policy)
// 步骤三:生成 Signature
let qSignature = HmacSHA1(stringToSign, signKey)
let credentials = {
policyObj: JSON.parse(policy),
policy: base64(policy),
qSignAlgorithm,
qAk: opt.SecretId,
qKeyTime,
qSignature,
}
return credentials
}
// v5 签名
const CosAuth = function (opt) {
if (!opt.SecretId) return console.error('missing param SecretId')
if (!opt.SecretKey) return console.error('missing param SecretKey')
if (opt.Version === '4.0') {
return CosAuthV4(opt)
} else if (opt.Version === 'post-object-policy') {
return CosAuthPolicy(opt)
}
opt = opt || {}
let SecretId = opt.SecretId
let SecretKey = opt.SecretKey
let method = (opt.Method || 'get').toLowerCase()
let query = opt.Query || {}
let headers = opt.Headers || {}
let pathname = opt.Pathname || '/'
let expires = opt.Expires
const getObjectKeys = function (obj) {
let list = []
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
list.push(key)
}
}
return list.sort((a, b) => {
a = a.toLowerCase()
b = b.toLowerCase()
return a === b ? 0 : (a > b ? 1 : -1)
})
}
const obj2str = function (obj, lowerCaseKey) {
let i, key, val
let list = []
let keyList = getObjectKeys(obj)
for (i = 0; i < keyList.length; i++) {
key = keyList[i]
val = (obj[key] === undefined || obj[key] === null) ? '' : (`${obj[key]}`)
key = lowerCaseKey ? camSafeUrlEncode(key).toLowerCase() : camSafeUrlEncode(key)
val = camSafeUrlEncode(val) || ''
list.push(`${key}=${val}`)
}
return list.join('&')
}
// 签名有效起止时间
let now = parseInt(new Date().getTime() / 1000) - 1
let exp = now + (expires === undefined ? 900 : (expires * 1 || 0)) // 默认签名过期时间为当前时间 + 900s
// 要用到的 Authorization 参数列表
let qSignAlgorithm = 'sha1'
let qAk = SecretId
let qSignTime = `${now};${exp}`
let qKeyTime = `${now};${exp}`
let qHeaderList = getObjectKeys(headers).join(';').toLowerCase()
let qUrlParamList = getObjectKeys(query).join(';').toLowerCase()
// 签名算法说明文档:https://www.qcloud.com/document/product/436/7778
// 步骤一:计算 SignKey
let signKey = HmacSHA1(qKeyTime, SecretKey)
// 步骤二:构成 FormatString
let formatString = [method, pathname, obj2str(query, true), obj2str(headers, true), ''].join('\n')
// 步骤三:计算 StringToSign
let stringToSign = ['sha1', qSignTime, SHA1(formatString), ''].join('\n')
// 步骤四:计算 Signature
let qSignature = HmacSHA1(stringToSign, signKey)
// 步骤五:构造 Authorization
let authorization = [
`q-sign-algorithm=${qSignAlgorithm}`,
`q-ak=${qAk}`,
`q-sign-time=${qSignTime}`,
`q-key-time=${qKeyTime}`,
`q-header-list=${qHeaderList}`,
`q-url-param-list=${qUrlParamList}`,
`q-signature=${qSignature}`,
].join('&')
return authorization
}
CosAuth.HmacSHA1 = HmacSHA1
CosAuth.SHA1 = SHA1
CosAuth.base64 = base64
export default CosAuth
可以参考这个官网的小程序直传文档。 https://cloud.tencent.com/document/product/436/34929
当前 SDK 代码里也有示例: https://github.com/tencentyun/cos-wx-sdk-v5/blob/master/demo/demo-post-policy.js
这个包,我是安装在小程序的主包里的,众所周知,小程序每个包大小最多只有 2MB,这个上传的包一下子就占了 1/10 多了,感觉太大了,希望优化一下。
另外希望提供 typescript 类型支持,不然写代码都无法感应类型。