- 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>