import request from '@/utils/request'; import Taro from '@tarojs/taro' import dayjs from 'dayjs'; import crypto from 'crypto-js'; import {Base64} from 'js-base64'; import {FileRecord} from "@/api/system/file/model"; import {TenantId} from "@/config/app"; export async function uploadOssByPath(filePath: string) { return new Promise(async (resolve) => { const date = new Date(); date.setMinutes(date.getMinutes() + 1); const policyText = { expiration: date.toISOString(), // 设置policy过期时间。 conditions: [ // 限制上传大小。 ["content-length-range", 0, 1024 * 1024 * 1024], ], }; let sts = Taro.getStorageSync('sts'); const stsExpired = Taro.getStorageSync('stsExpiredAt'); if (!sts || (stsExpired && dayjs().isBefore(dayjs(stsExpired)))) { // @ts-ignore const {data: {data: {credentials}}} = await request.get(`https://shop-api.websoft.top/api/oss/getSTSToken`) Taro.setStorageSync('sts', credentials) Taro.setStorageSync('stsExpiredAt', credentials.expiration) sts = credentials } const [filename, ext] = filePath.split('.') const key = `headers/${filename + (Math.random() * 100000)}.${ext}` const policy = Base64.encode(JSON.stringify(policyText)) const signature = computeSignature(sts.accessKeySecret, policy) Taro.uploadFile({ url: 'https://oss.wsdns.cn', name: 'file', filePath, formData: { key, policy, OSSAccessKeyId: sts.accessKeyId, signature, 'x-oss-security-token': sts.securityToken }, success: () => { resolve(`https://oss.wsdns.cn/${key}`) } }) }) } const computeSignature = (accessKeySecret: string, canonicalString: string): string => { return crypto.enc.Base64.stringify(crypto.HmacSHA1(canonicalString, accessKeySecret)); } /** * 在调用涉及用户隐私的 API(chooseImage / chooseMedia / getLocation / getPhoneNumber 等)前, * 等待用户完成微信隐私协议授权。基础库 3.16.1+ 强制要求,未授权会抛 errno:112。 * 旧基础库或不支持隐私协议的版本直接放行。 */ export const ensurePrivacyAuthorized = (): Promise => { return new Promise((resolve) => { const wxAny: any = Taro if (typeof wxAny.requirePrivacyAuthorize !== 'function') { resolve() return } if (typeof wxAny.getPrivacySetting === 'function') { wxAny.getPrivacySetting({ success: (res: any) => { if (res && res.needAuthorization) { wxAny.requirePrivacyAuthorize({ success: () => resolve(), fail: () => resolve(), }) } else { resolve() } }, fail: () => resolve(), }) } else { wxAny.requirePrivacyAuthorize({ success: () => resolve(), fail: () => resolve(), }) } }) } /** * 上传阿里云OSS */ export async function uploadFile() { return new Promise(async (resolve: (result: FileRecord) => void, reject) => { // 修复:基础库 3.16.1+ 强制隐私协议校验,先确保用户已同意 try { await ensurePrivacyAuthorized() } catch { // 忽略前置授权错误,让 chooseImage 自己抛具体的 fail } Taro.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: async (res) => { const tempFilePath = res.tempFilePaths[0]; // 上传图片到OSS Taro.uploadFile({ url: 'https://server.websoft.top/api/oss/upload', filePath: tempFilePath, name: 'file', header: { 'content-type': 'application/json', TenantId }, success: (res) => { try { const data = JSON.parse(res.data); if (data.code === 0) { resolve(data.data) } else { reject(new Error(data.message || '上传失败')) } } catch (error) { reject(new Error('解析响应数据失败')) } }, fail: (err) => { reject(new Error('上传请求失败')) } }) }, fail: (err) => { reject(new Error('选择图片失败')) } }); }) }