飞书/微信小程序直传阿里云上传

151 阅读1分钟

阿里云配置

STS配置

STS上传主要参数

{
    host: r.host,
    OSSAccessKeyId: r.accessKeyId,
    policy: policy,
    signature: computeSignature(r.accessKeySecret, policy),
    "x-oss-security-token": r.securityToken,
    dir: r.dir
}

功能描述

  • 点击:选择上传文件 选择文件
  • 获取 STS凭证
  • 上传阿里云
  • 成功后下方会出现上传列表
  • 支持点击删除进行列表操作

效果展示

Jun-18-2024 16-39-20.gif

1. 获取STS凭证及上传

// signature 处理
function computeSignature(accessKeySecret, canonicalString) {
  return crypto.enc.Base64.stringify(crypto.HmacSHA1(canonicalString, accessKeySecret))
}

export function ossUpload(_files) {
  if (!_files) return Promise.resolve({ status: "fail" })
  const files = Array.isArray(_files) ? [..._files] : [{ ..._files }]

  return getPlmAppStsToken()  // 获取STS接口
    .then((r) => {
      const policy = Base64.encode(
        JSON.stringify({
          expiration: r.expiration, // 设置policy过期时间。
          conditions: [
            // 限制上传大小。
            ["content-length-range", 0, 1024 * 1024 * 1024],
          ],
        })
      ) // policy必须为base64的string。
      return {
        host: r.host,
        OSSAccessKeyId: r.accessKeyId,
        policy: policy,
        signature: computeSignature(r.accessKeySecret, policy),
        "x-oss-security-token": r.securityToken, // 需要注意后端的TOKEN是否加密
        dir: r.dir,
      }
    })
    .then((params) => {
      return Promise.all([
        ...files.map((i, index) => {
          const fileExtension = i.name.split(".").pop(); // 获取文件后缀
          const fileKey = `${moment().valueOf()}${index}${fileExtension}` // 设置唯一KEY
          return new Promise((resolve, reject) => {
            uni.uploadFile({  // 这里是微信的上传,如果是飞书则使用tt.uploadFile
              url: params.host,
              filePath: i.path,
              name: "file", // 这个必须使用这个字段
              formData: {
                ...params,
                key: fileKey,
              },
              success: (result) => {
                const { statusCode } = result
                if (statusCode === 204) {
                  resolve({
                    ...i,
                    key: fileKey,
                    imgUrl: params.host + "/" + fileKey // 对数据进行处理,如果是防盗链,需要单独处理,否则获取不到图片的正确地址
                  })
                } else {
                  reject({ status: "fail", err: result })
                }
              },
              fail: (e) => reject({ status: "fail", err: e }),
            })
          })
        }),
      ])
    })
    .then((r) => ({ status: "success", files: r }))
}

2. 打开文件列表

2.1. 飞书小程序

const fileUpload = () => {
  // 飞书小程序时,使用
  // #ifdef MP-LARK
  tt.filePicker({
    maxNum: 10,
    pickerTitle: "选择一个文件", // isSyStem 为TRUE时生效
    pickerConfirm: "确认选择", // isSyStem 为TRUE时生效
    isSystem: true, // true 打开系统文件列表,false 打开飞书文件列表
    success: async (res) => {
     // 2.3 数据处理
    },
    fail(res) {
      console.log(`读取失败: ${JSON.stringify(res)}`)
    },
  })
  // #endif
}

2.2. 微信小程序

  • 可以直接使用 uni-file-picker
 <uni-file-picker
      v-model="form.imageValue"
      fileMediatype="image"
      mode="grid"
      :autoUpload="false"
      @select="select"
      @delete="deleteImg"
/>
  • 使用chooseMediachooseImage
  // #ifdef MP-WEIXIN
  // 调用拍照/选择图片
  uni.chooseMedia({
    // 文件个数
    count: 1,
    // 文件类型
    mediaType: ['image'],
    success: ({ tempFilePaths, tempFiles }) => {
      // 本地路径
      ossUpload(res.tempFilePaths)
    },
  })
  // #endif
  
  // #ifdef H5 || APP-PLUS
  uni.chooseImage({
    count: 1,
    success: ({ tempFilePaths, tempFiles }) => {
      ossUpload(tempFilePaths)
    },
    fail: (error) => {},
  })
  // #endif

2.3. 数据处理

const fileList = reactive([])

...
 success: async (res) => {
      const result = await ttOssUpload(res?.list)
      const files = JSON.parse(JSON.stringify(result)).files
      files.forEach((file) => {
        fileList.push(file)
      })
},
...

// 文件删除
const handleButtonClick = (index) => {
  fileList.splice(index, 1)
}

2.4. 按钮触发及列表渲染操作

  <view  @tap="fileUpload">选择上传文件</view>
        <view class="file-list">
              <view class="file-item" 
                  v-for="(item, index) in fileList" 
                  :key="item.imgUrl"
                 >
                  <label class="name"> {{ item.name }}</label>
                  <label class="delete" 
                      @tap="handleButtonClick(index)"
                  >删除</label>
              </view>
         </view>
 </view>
 
 ...
 
 // 文件列表样式
  . file -item {
    display: flex;
    justify-content: space-between;
    padding: 4rpx;
    margin: 4rpx;
    background: #f5f3f3;
    . name {
      color: #27ba9b;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      max-width: calc(100% - 80rpx);
    }
    . delete {
      color: #f00;
    }
  }