tencentyun / cos-wx-sdk-v5

腾讯云 COS 小程序 SDK(XML API)
https://cloud.tencent.com/product/cos
MIT License
192 stars 484 forks source link

压缩版本还是太大,有227kb,能否继续优化一下?, 另外希望提供typescript dts支持。 #85

Closed sonofmagic closed 1 year ago

sonofmagic commented 1 year ago

这个包,我是安装在小程序的主包里的,众所周知,小程序每个包大小最多只有 2MB,这个上传的包一下子就占了 1/10 多了,感觉太大了,希望优化一下。

image

另外希望提供 typescript 类型支持,不然写代码都无法感应类型。

akFace commented 1 year ago

同遇到问题,真的就他最大了

akFace commented 1 year ago

真的离谱,就属他最大 image

sonofmagic commented 1 year ago

O(∩_∩)O哈哈~,是的,这个 issue 也提了 1 个月多了也没人理,好蛋疼。

akFace commented 1 year ago

O(∩_∩)O哈哈~,是的,这个 issue 也提了 1 个月多了也没人理,好蛋疼。

我自己写了一个,非常简单的。不用他这个了 可以参考这个来实现 https://www.tencentcloud.com/zh/document/product/436/30934

akFace commented 1 year ago

@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)
      }); 
  }
})
sonofmagic commented 1 year ago

感谢大佬分享,@carsonxu @livehigh 参考一下大佬的思路,优化一下这个 sdk

livehigh commented 1 year ago

感谢大佬分享,@carsonxu @livehigh 参考一下大佬的思路,优化一下这个 sdk

收到大家的反馈,小程序sdk因为提供了多种api以及内置了部分api调用需要的依赖库,导致整体文件偏大。后续我们会针对这些依赖库寻找更小体积的替代品做优化。 当您的业务仅仅需要上传功能且不需要使用分块上传时,可以不使用sdk,用简单上传的方式即可。 https://github.com/tencentyun/cos-wx-sdk-v5/issues/85

transtone commented 1 year ago

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
carsonxu commented 1 year ago

可以参考这个官网的小程序直传文档。 https://cloud.tencent.com/document/product/436/34929

当前 SDK 代码里也有示例: https://github.com/tencentyun/cos-wx-sdk-v5/blob/master/demo/demo-post-policy.js