/** * COS直传工具 * 通过PUT预签名URL将文件直传到腾讯云COS * 使用 uni.getFileSystemManager().readFile 读取文件二进制数据 * 再通过 uni.request PUT 方式上传(uni.uploadFile 只支持 POST) */ import { getPresignedUploadUrl } from '@/api/user.js' /** * 选择图片并上传到COS * @param {Object} [options] - 选项 * @param {number} [options.count=1] - 选择数量 * @param {string[]} [options.sourceType] - 来源类型 * @returns {Promise} 上传后的文件URL */ export async function chooseAndUploadImage(options = {}) { const { count = 1, sourceType = ['album', 'camera'] } = options // 1. 选择图片 const chooseRes = await new Promise((resolve, reject) => { uni.chooseImage({ count, sizeType: ['compressed'], sourceType, success: resolve, fail: (err) => { if (err.errMsg && err.errMsg.includes('cancel')) { reject(new Error('用户取消选择')) } else { reject(new Error('选择图片失败')) } } }) }) const tempFilePath = chooseRes.tempFilePaths[0] const fileName = tempFilePath.split('/').pop() || 'image.png' // 判断文件类型 const ext = fileName.split('.').pop()?.toLowerCase() || 'png' const mimeMap = { jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', gif: 'image/gif', webp: 'image/webp' } const contentType = mimeMap[ext] || 'image/png' // 2. 获取预签名URL const presignedRes = await getPresignedUploadUrl(fileName, contentType) if (!presignedRes || presignedRes.code !== 0 || !presignedRes.data) { throw new Error(presignedRes?.message || '获取上传地址失败') } const { uploadUrl, fileUrl } = presignedRes.data // 3. 读取文件二进制数据,然后用PUT方式上传到COS await new Promise((resolve, reject) => { uni.getFileSystemManager().readFile({ filePath: tempFilePath, success: (readRes) => { // 使用PUT方法上传二进制数据 uni.request({ url: uploadUrl, method: 'PUT', data: readRes.data, header: { 'Content-Type': contentType }, success: (res) => { if (res.statusCode === 200) { resolve(res) } else { console.error('COS上传失败:', res.statusCode, res.data) reject(new Error(`上传失败,状态码: ${res.statusCode}`)) } }, fail: (err) => reject(new Error('上传COS失败: ' + (err.errMsg || '网络错误'))) }) }, fail: (err) => reject(new Error('读取文件失败: ' + err.errMsg)) }) }) // 4. 返回文件访问URL return fileUrl }