import { post } from './api' /** * 从后端获取 COS 预签名上传 URL * @param {number} count - 文件数量 * @param {string} ext - 文件扩展名 * @returns {Promise>} */ export async function getPresignUrls(count, ext = '.jpg') { const res = await post('/business/CosUpload/presignUrl', { count, ext }, { timeout: 120000 }) if (res.code !== 200) { throw new Error(res.msg || '获取上传地址失败') } return res.data } /** * 通过预签名 URL 直传文件到 COS * @param {string} presignUrl - PUT 预签名 URL * @param {string} filePath - 本地文件路径 * @returns {Promise} */ export function uploadToCos(presignUrl, filePath) { return new Promise((resolve, reject) => { // #ifdef H5 _uploadH5(presignUrl, filePath).then(resolve).catch(reject) // #endif // #ifdef APP-PLUS _uploadApp(presignUrl, filePath).then(resolve).catch(reject) // #endif }) } // #ifdef H5 async function _uploadH5(presignUrl, filePath) { // H5 端 filePath 可能是 base64 或 blob URL let blob if (filePath.startsWith('data:')) { const resp = await fetch(filePath) blob = await resp.blob() } else { const resp = await fetch(filePath) blob = await resp.blob() } const res = await fetch(presignUrl, { method: 'PUT', headers: { 'Content-Type': 'image/jpeg' }, body: blob }) if (!res.ok) { throw new Error(`COS上传失败: ${res.status}`) } } // #endif // #ifdef APP-PLUS function _uploadApp(presignUrl, filePath) { return new Promise((resolve, reject) => { console.log('[COS] APP端开始读取文件:', filePath) plus.io.resolveLocalFileSystemURL(filePath, (entry) => { entry.file((file) => { console.log('[COS] 文件大小:', file.size, 'bytes') const reader = new plus.io.FileReader() reader.onloadend = (e) => { const base64Data = e.target.result const pure = base64Data.split(',')[1] const binary = atob(pure) const len = binary.length const bytes = new Uint8Array(len) for (let i = 0; i < len; i++) { bytes[i] = binary.charCodeAt(i) } console.log('[COS] 文件读取完成, 准备PUT上传, 数据大小:', len, 'bytes') // 使用 uni.request 发送 PUT,APP端走原生网络,支持 ArrayBuffer uni.request({ url: presignUrl, method: 'PUT', header: { 'Content-Type': 'image/jpeg' }, data: bytes.buffer, timeout: 120000, success(res) { console.log('[COS] PUT响应, statusCode:', res.statusCode) if (res.statusCode >= 200 && res.statusCode < 300) { resolve() } else { reject(new Error(`COS上传失败: ${res.statusCode}`)) } }, fail(err) { console.error('[COS] PUT请求失败:', JSON.stringify(err)) reject(new Error('COS上传网络错误: ' + (err.errMsg || ''))) } }) } reader.onerror = () => { console.error('[COS] 文件读取失败') reject(new Error('读取文件失败')) } reader.readAsDataURL(file) }) }, (err) => { console.error('[COS] 解析文件路径失败:', JSON.stringify(err)) reject(new Error('解析文件路径失败: ' + JSON.stringify(err))) }) }) } // #endif