oss(阿里云)前端直传

发布于:2025-08-17 ⋅ 阅读:(13) ⋅ 点赞:(0)
  • WEB端前端直传

参考文档:web前端直传并设置上传回调

封装oss-upload.ts

// 图片上传
import { uploadToken } from '@/api/uploadFile.js' // 获取oss token接口

// 定义 OSS 信息类型
interface OssInfo {
  policy: string;
  signature: string;
  x_oss_credential: string;
  x_oss_date: string;
  dir: string;
  security_token: string;
  callback: string;
  host: string;
  data?: {
    filename?: string;
    url?: string;
  };
}

// 定义 uploadToken 返回数据类型
interface UploadTokenResponse {
  status_code: number;
  data: OssInfo;
}

let ossInfo: OssInfo | null = null

// 定义文件类型
type FileWithMetadata = File & {
  base64?: string;
  width?: number;
  height?: number;
  url?: string;
  fileType?: string;
  raw?: File;
  thumbdata?: string;
};

/**
 * 文件上传数组
 * 过滤已上传文件
 * @param {FileWithMetadata[]} files 要上传的文件数组
 * @returns {Promise<{ sucArr: FileWithMetadata[]; failArr: FileWithMetadata[] }>} 包含上传成功和失败文件的Promise
 */
export async function fileUpload(files: FileWithMetadata[]): Promise<{ sucArr: FileWithMetadata[]; failArr: FileWithMetadata[] }> {
  const sucArr: FileWithMetadata[] = []
  const failArr: FileWithMetadata[] = []
  return new Promise((resolve) => {
    const result = files.reduce((accumulatorPromise: Promise<void>, next: FileWithMetadata) => {
      return accumulatorPromise.then(() => { // 上一个接口执行完毕再执行下一个
        if (!next.url || !next.url.includes(import.meta.env.VITE_APP_UPLOAD_API)) {
          return uploadItemHandle((next.fileType === 'file' && next.raw) ? next.raw : next).then((res) => {
            sucArr.push(res)
          }).catch((res) => {
            failArr.push(res)
          })
        } else {
          sucArr.push(next)
        }
      })
    }, Promise.resolve())

    result.then(() => {
      console.log('All Promises Resolved', { sucArr, failArr })
      resolve({ sucArr, failArr })
    })
  })
}

/**
 * 文件处理
 * @param {FileWithMetadata} file 要上传的文件
 * @returns {Promise<FileWithMetadata>} 包含上传成功文件的Promise
 */
function uploadItemHandle(file: FileWithMetadata): Promise<FileWithMetadata> {
  return new Promise(async(resolve, reject) => {
    if (!ossInfo) { // 减少调用接口次数
      const data = await uploadToken() as UploadTokenResponse
      if (Number(data.status_code) === 200) {
        ossInfo = data.data
      }
    }
    if (!ossInfo) return reject(file)
    const formData = new FormData()
    formData.append('success_action_status', '200')
    formData.append('policy', ossInfo.policy)
    formData.append('x-oss-signature', ossInfo.signature)
    formData.append('x-oss-signature-version', 'OSS4-HMAC-SHA256')
    formData.append('x-oss-credential', ossInfo.x_oss_credential)
    formData.append('x-oss-date', ossInfo.x_oss_date)
    formData.append('key', ossInfo.dir + file.name) // 文件名
    formData.append('x-oss-security-token', ossInfo.security_token)
    formData.append('callback', ossInfo.callback) // 添加回调参数
    formData.append('file', file) // file 必须为最后一个表单域

    try {
      const response = await fetch(ossInfo.host, { method: 'POST', body: formData })
      if (!response.ok) throw new Error(`请求失败: ${response.status}`)
      const imgData = await response.json()
      if (imgData.status === 200 && imgData.data) {
        file.thumbdata = imgData.data.filename || ''
        file.url = imgData.data.url || ''
        resolve(file)
      } else {
        console.error(JSON.stringify(imgData))
        reject(file)
      }
    } catch (error) {
      console.error(error)
      reject(file)
    }
  })
}

调用:

await fileUpload(list).then(arr => {
  if (arr.failArr.length === 0) {
    console.log('上传数据返回:',arr)
  } else {
    ElMessage({ message: `有文件上传失败请重新上传!`, type: 'error', duration: 2 * 1000 })
  }
}).catch(() => {
  return { sucArr: [] }
})

微信小程序端
参考文档:微信小程序端前端直传
ossUpload.js

(function () {
  const {
    request
  } = require('./request')
  let ossInfo = null

  function uploadFile(filePath, success, fail, options, progress, cancelTask) {
    let successResult = []
    let failResult = []
    const result = filePath.reduce((accumulatorPromise, next) => {
      return accumulatorPromise.then(() => { // 上一个接口执行完毕再执行下一个
        return uploadItem(next).then((res => {
          successResult = successResult.concat(res)
        })).catch(res => {
          failResult = failResult.concat(res)
        })
      })
    }, Promise.resolve())
    result.then(e => {
      if (failResult.length === 0) success(successResult)
      else fail(successResult)
    })
  }

  // 正式上传的前置方法,做预处理
  function uploadItem(file) {
    return new Promise(async (resolve, reject) => {
      if (!ossInfo) {
        const data = await getOssToken()
        if (Number(data.status_code) === 200) {
          ossInfo = data.data
          ossInfo = data
        }
      }
      console.log('file',file)
      if (!ossInfo) return reject(file)

      // 上传参数
      const formData = {
        key:file.tempFilePath.split('/').pop(), //上传文件名称
        policy: ossInfo.policy, //表单域
        'x-oss-signature-version': ossInfo.xOssSignatureVersion, //指定签名的版本和算法
        'x-oss-credential': ossInfo.xOssCredential, //指明派生密钥的参数集
        'x-oss-date': ossInfo.xOssDate, //请求的时间
        'x-oss-signature': ossInfo.xOssSignature, //签名认证描述信息
        'x-oss-security-token': ossInfo.xOssSecurityToken, //安全令牌
        success_action_status: ossInfo.success_action_status //上传成功后响应状态码
      };
      // 发送请求上传文件 
      wx.uploadFile({
        // Bucket域名 请替换为目标Bucket域名
        url: ossInfo.base_url, // 此域名仅作示例,实际Bucket域名,请替换为您的目标Bucket域名。
        filePath: file.tempFilePath,
        name: 'file', //固定值为file
        formData: formData,
        success(res) {
          console.log('上传响应:', res);
          if (res.statusCode === 200) {
            resolve(res.data); // 上传成功
          } else {
            console.error('上传失败响应:', res);
            reject(res); // 上传失败,返回响应
          }
        },
        fail(err) {
          console.error('上传失败:', err); // 输出错误信息
          reject(err); // 调用回调处理错误
        }
      });
    })
  }
  // 获取阿里云token
  function getOssToken() {
    return new Promise((resolve, reject) => {
      const data = {
        url: 'api/sts-token', // 获取oss token接口
        method: 'post'
      }
      request(data, true, false, '操作中...', 2000)
        .then((res) => {
          resolve(res)
        })
        .catch((err) => {
          reject('token获取失败' + err)
        })
    })
  }
  module.exports = {
    uploadFile
  }
})();

使用:
index.js

const {uploadFile} = require('../../utils/ossUpload')

Component({
  data: {
    image: [],
  },
  pageLifetimes: {
    show() {
      if (typeof this.getTabBar === 'function') {
        this.getTabBar((tabBar) => {
          tabBar.setData({
            selected: 0
          })
        })
      }
    }
  },
  methods: {
    onUpload() {
      uploadFile(this.data.image, (res) => {
        console.log('成功', res)
        this.onSubmit()
      }, (err) => {
        console.log('失败', err)
      })
    },
    onSubmit() {
      console.log('好啦')
    },
    uploadImg() {
      wx.chooseMedia({
        count: 9,
        mediaType: ['image', 'video'],
        sourceType: ['album', 'camera'],
        maxDuration: 30,
        camera: 'back',
        success: (res) => {
          console.log(res.tempFiles)
          this.setData({
            image: res.tempFiles
          })
        }
      })
    }
  }
})

index.wxml:

<button bind:tap="uploadImg">上传</button>
<image wx:for="{{image}}" src="{{item.tempFilePath}}" mode="" />
<button bind:tap="onUpload">提交</button>

网站公告

今日签到

点亮在社区的每一天
去签到