在工作中遇到了需要小程序上传到AWS的问题。使用的是uni-app, v2。

最开始搜索了方法,使用插件“aws-sdk”,配合预签名URL 进行使用,后来报错,发现小程序的上传并不支持这个插件的使用,过程忘记保留了。

在社群论坛中看到一篇文章,发现------

只能使用最原始的uni.uploadFile()进行上传,只是需要配置不同的参数。

记录一下代码,关于原理和逻辑,还是多参考aws的官网

// 生成AWS S3 POST请求签名 --- 需要添加引入
import crypto from 'crypto-js'

这个方法的目的是生成一个签名密钥,以便用于请求签名。这是AWS Version 4签名过程的一部分。签名密钥是从您的AWS secret access key以及其他几个参数(日期、区域和服务)计算出来的。最后,这个签名密钥会用于计算签名哈希,该哈希会附加到请求中,以便AWS可以验证发送者的身份。 请注意,在实际使用中,必须更详细地处理错误和边缘情况,比如输入参数的验证等等。此外,如果你的代码将在浏览器环境中运行,可能需要使用一个不同的加密库,因为crypto库是Node.js特有的。

generateS3PostSignature() {
  const dateStamp = dayjs().utc().format('YYYYMMDD');
  const regionName = 'us-east-1'; // 可以替换自己的region
  const serviceName = 's3';
  const secretKey = 'xxxxxxxxxxxxxxx'; // 使用自己的secretKey
  // 将policy转换为base64编码 --- policy政策 后文会提到,可修改
  this.policyBase64 = Buffer.from(JSON.stringify(this.policy)).toString('base64');
  // 获取签名密钥 ---自定义的方法 getSignatureKey
  const signingKey = this.getSignatureKey(secretKey, dateStamp, regionName, serviceName);
  // 计算签名
  const signature = crypto.HmacSHA256(this.policyBase64, signingKey).toString(crypto.enc.Hex);
  return signature;
},

 // 获取signingKey的方法 --- getSignatureKey  

  getSignatureKey(key, dateStamp, regionName, serviceName) {
      const kDate = crypto.HmacSHA256(dateStamp, 'AWS4' + key);
      const kRegion = crypto.HmacSHA256(regionName, kDate);
      const kService = crypto.HmacSHA256(serviceName, kRegion);
      const kSigning = crypto.HmacSHA256('aws4_request', kService);
      return kSigning;
    },

// 此段代码格式参考了 Aws官方文档的方法

下边就是选择图片和上传图片或视频的内容了,顺序很重要,顺序很重要,顺序很重要~~

chooseImage(filePath, fileType, thumbTempFilePath) {
      //thumbTempFilePath 视频的封面
      if (fileType === 'video') {
        this.imgTemporaryAddress = thumbTempFilePath
      }
      this.isUploadImg = true // 是否正在上传的flag
      this.imgUuid = getUUID() 
      const date = dayjs().utc().format('YYYYMMDDTHHmmss') + 'Z';
      this.policy = {
        "expiration": "2099-12-30T12:00:00.000Z",// 截止日期
        "conditions": [
          {"bucket": "your-bucket"}, // 自己的bucket包名
          ["starts-with", "$key", ""],
          {"acl": "public-read"}, // 一定要加,之前没加,有报错
          ["starts-with", "$Content-Type", fileType], // fileType 是从子组件传入的 可以是image 或者 video
          {"x-amz-meta-uuid": this.imgUuid}, // 唯一性
          {"x-amz-server-side-encryption": "AES256"},
          ["starts-with", "$x-amz-meta-tag", ""],
          {"x-amz-credential": `密钥id/${dayjs().utc().format('YYYYMMDD')}/us-east-1/s3/aws4_request`}, // 使用密钥id进行拼接,/us-east-1/s3/ 这两个值要和之前使用的保持一直
          {"x-amz-algorithm": "AWS4-HMAC-SHA256"},
          {"x-amz-date": date}
        ]
      }
      let index = filePath.lastIndexOf('/');
      let fileKey = filePath.substring(index + 1);  // 自己项目需要上传的字段
      const secretAccessKey = 'your—secretAccessKey' // 传入自己的secretAccessKey 
      const signature = this.generateS3PostSignature(secretAccessKey) // 之前已经写过的方法
      const formData = {
        "key": fileKey,
        "bucket": "your-bucket",  // your-bucket
        'policy': this.policyBase64, // 加密的政策信息
        "Content-Type": fileType, // 文件类型
        "acl": "public-read", // 添加ACL字段 !!一定要加
        "x-amz-server-side-encryption": "AES256", // 加密方式,
        "x-amz-meta-uuid": this.imgUuid,
        // 拼接了实际访问的密钥ID
        "x-amz-credential": `实际访问的密钥ID/${dayjs().utc().format('YYYYMMDD')}/us-east-1/s3/aws4_request`,
        "x-amz-algorithm": "AWS4-HMAC-SHA256",
        // "x-amz-content-sha256": "UNSIGNED-PAYLOAD",
        "x-amz-meta-tag": "",
        "x-amz-date": date,
        "X-Amz-Signature": signature,
      }
      // 使用uni.uploadFile上传文件到服务器
      const uploadTask = uni.uploadFile({
      url: 'https://xxxxx.xxxx.com/', // 替换为处理文件上传的接口地址
        filePath: filePath,
        name: 'file',
        formData: formData, // 之前的formData
        success: res => {
          if (res.success) {
            this.isUploadImg = false  // 关闭上传状态
            // 文件上传到AWS成功,处理响应结果,进行自己的逻辑操作
            
          } else {
            uni.showToast({
              title: '图片发送失败',
              icon: 'none',
              duration: 2500
            })
            this.isUploadImg = false
          }
          console.log(res, 'res')
        },
        fail: error => {
          this.isUploadImg = false
          console.error('文件上传失败', error)
        }
      })
      uploadTask.onProgressUpdate((res) => {
        this.uploadProgress = res.progress // 获取上传的进度 res的信息很多可以自行提取
       
        //   uploadTask.abort(); //  取消上传任务。
      })
    },

在上传成功之前,会有各种各样的问题~ 

关于算法的选择和使用,参考了Aws的建议

 关于报错的展示,需要关注Code 和 Message标签的内容,只要是报错,都能去解决。

写这个文件最开始的时候,问题很多,完全没有报错展示,就是上传不了。

下图是AWS请求签名计算有问题,导致它与AWS期望的签名不匹配。

出现该问题可能是因为

  1. 密钥错误:你提供的AWS密钥(包括Access key和Secret key)可能是错误的。请确认你使用的密钥是否正确。

  2. 签名计算错误:你可能在计算签名的过程中犯了错误。你的签名应该是用你的AWS Secret Access Key、日期、区域、服务和特定的签名字符串(在你的代码中是"aws4_request")进行的散列。请确保这些值都是正确的,并且你正确地进行了计算。

  3. 时间错误:如果你的服务器时间和AWS服务器时间有较大的偏差,那么这也可能导致签名验证失败。确保你的服务器时间是正确的。

  4. 请求参数或头部错误:你在请求中设置的一些参数或者头部可能影响到了签名的计算。比如,如果你在计算签名的时候包含了某个头部,但是在实际的请求中没有包含这个头部,那么就会导致签名错误。你需要确保计算签名时使用的参数和头部与实际的请求完全一致。

当时卡在了policy对象编码成base64的问题~

 

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐