import request from "./request"; let loading = false; /** * 核心新增:校验图片比例是否符合 4:3 规范(带容错) * @param {Number} width 图片宽度 * @param {Number} height 图片高度 * @param {Number} tolerance 容错值,默认0.05,摇摆空间足够 * @returns {Boolean} 是否符合比例 */ const checkImageRatio = (width, height, tolerance = 0.05) => { const standardRatio = 4 / 3; // 标准4:3比例 const actualRatio = width / height; // 实际图片宽高比 // 支持 横版4:3 或 竖版3:4 两种情况 console.log(actualRatio, standardRatio) //const isStandardHorizontal = Math.abs(actualRatio - standardRatio) <= tolerance; // const isStandardVertical = Math.abs(actualRatio - (3 / 4)) <= tolerance; // return isStandardHorizontal || isStandardVertical; const isStandardHorizontal = standardRatio - actualRatio <= 0.334 || actualRatio - standardRatio <= tolerance; return isStandardHorizontal; }; // 触发上传 const handleUpload = async (userId, type, sourceType = ['album', 'camera'], uploadType = 'image') => { if (loading) return try { loading = true let file = null if (uploadType == 'image') { const res = await uni.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: sourceType }) console.log('res======', res) // 检查用户是否真的选择了图片 if (!res || !res.tempFiles || res.tempFiles.length === 0) { console.log('用户取消了图片选择') return null // 明确返回 null 表示用户取消 } file = res.tempFiles[0] // ========== 【核心新增:图片比例校验 开始】 ========== // 获取图片宽高信息(uniapp多端兼容API,APP/小程序/H5都支持) const imageInfo = await uni.getImageInfo({ src: file.path }); const { width, height } = imageInfo; console.log('图片宽高:', width, height, '实际比例:', (width / height).toFixed(4)); // 校验4:3比例,容错0.05 const isRatioValid = checkImageRatio(width, height, 0.05); if (!isRatioValid) { uni.showToast({ title: '图片比例需为4:3,允许轻微偏差', icon: 'none', duration: 2000 }); return null; // 比例不符,终止上传流程 } // ========== 【核心新增:图片比例校验 结束】 ========== } else if (uploadType == 'video') { const res = await uni.chooseVideo({ count: 1, sourceType: ['album', 'camera'], compressed: false }) // #ifdef H5 file = res.tempFile; // #endif // #ifdef APP-PLUS || MP-WEIXIN file = res; // #endif } const result = await uploadFile(file, userId, type, uploadType) return result; } catch (error) { console.error('上传出错:', error) // 如果是用户取消操作,不抛出错误,返回 null if (error.errMsg && error.errMsg.includes('cancel')) { console.log('用户取消了操作') return null } throw error } finally { loading = false } } // iOS专用:HEIC转JPG const convertHEICtoJPG = async (heicPath) => { return new Promise((resolve, reject) => { plus.zip.compressImage({ src: heicPath, dst: heicPath.replace(/\.heic$|\.heif$/i, '.jpg'), format: 'jpg', quality: 90 }, resolve, reject) }) } // 获取二进制数据(兼容iOS) const getFileBinary = async (filePath) => { return new Promise((resolve) => { const reader = new plus.io.FileReader() reader.onloadend = (e) => { resolve(e.target.result) } reader.readAsArrayBuffer(filePath) }) } const compressAndUpload = async (file, userId, type) => { try { console.log('file=====', file) // 压缩图片 const compressedResult = await uni.compressImage({ src: file.path, quality: 80, // 压缩质量,范围0-100 format: 'jpg', // 指定输出为jpg格式 }); // 获取压缩后的临时文件路径 const compressedPath = compressedResult.tempFilePath; console.log('compressedPath', compressedPath) file.path = compressedPath; console.log('file.path', file.path) // 上传图片 const result = await uploadFile(file, userId, type) return result; } catch (error) { console.error('压缩或上传失败:', error); uni.showToast({ title: '图片处理失败', icon: 'none' }); } } // 上传文件到OSS const uploadFile = async (file, userId, type, uploadType) => { console.log('file=======', file) console.log('userId=======', userId) // 检查文件大小 if (uploadType == 'image') { if (file.size > 5 * 1024 * 1024) { uni.showToast({ title: `文件大小不能超过5MB`, icon: 'none' }) return Promise.reject(new Error('文件大小超出限制')) } } else if (uploadType == 'video') { if (file.size > 100 * 1024 * 1024) { uni.showToast({ title: `文件大小不能超过100MB`, icon: 'none' }) return Promise.reject(new Error('文件大小超出限制')) } } let date = new Date().getTime() console.log('file', file) // let imageType = file.path.split('.') let imageType; if (uploadType == 'image') { //imageType = file.path.split('.') imageType = file.path.split('.') } else if (uploadType == 'video') { // #ifdef H5 imageType = file.name.split('.') // #endif // #ifdef APP-PLUS || MP-WEIXIN imageType = file.tempFilePath.split('.') // #endif } let artisan = type == 1 ? 'yh' : type == 2 ? 'syr' : type == 3 ? 'sj' : ''; let name = `${userId}_${artisan}_${date}.${imageType[imageType.length - 1]}` console.log('file.path', file.path) console.log('imageType', imageType) console.log('name', name) try { // 1. 获取OSS上传凭证 const ossConfig = await getOssConfig(type) // 2. 上传文件 const uploadRes = await uploadToOss(file, ossConfig, name, uploadType) // 3. 添加到文件列表 const fileUrl = `${ossConfig.host}/${ossConfig.dir}${name}` return fileUrl } catch (error) { console.error('上传失败:', error) throw error } } // 获取OSS配置 const getOssConfig = async (type) => { try { const res = await request.post('/user/getalioss', { type: type, kind: 1 }) return res } catch (error) { console.error('获取OSS配置失败:', error) throw error } } // 上传到OSS const uploadToOss = (file, ossConfig, name, uploadType) => { console.log(ossConfig) return new Promise((resolve, reject) => { // #ifdef H5 const formData = new FormData() formData.append('name', name); formData.append('policy', ossConfig.policy); formData.append('OSSAccessKeyId', ossConfig.ossAccessKeyId); formData.append('success_action_status', '200'); formData.append('signature', ossConfig.signature); formData.append('key', ossConfig.dir + name); // file必须为最后一个表单域,除file以外的其他表单域无顺序要求。 formData.append('file', file); fetch(ossConfig.host, { method: 'POST', body: formData }, ).then((res) => { resolve(`${ossConfig.host}/${ossConfig.dir}${name}`) }).catch(() => { reject(new Error(`上传失败: ${res.statusCode}`)) }) // #endif // #ifdef APP-PLUS || MP-WEIXIN let formData = {}; formData.name = name; formData.policy = ossConfig.policy; formData.OSSAccessKeyId = ossConfig.ossAccessKeyId; formData.success_action_status = '200'; formData.signature = ossConfig.signature; formData.key = ossConfig.dir + name; formData.file = file; let path; if (uploadType == 'image') { path = file.path // 获取文件扩展名并设置 Content-Type console.log('111111'); const ext = file.path.split('.').pop().toLowerCase() console.log('33333333'); const mimeTypes = { jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', gif: 'image/gif' } if (mimeTypes[ext]) { formData['Content-Type'] = mimeTypes[ext], formData['x-oss-meta-content-type'] = mimeTypes[ext] } console.log('formData', formData) } else if (uploadType == 'video') { // #ifdef H5 path = file.name // #endif // #ifdef APP-PLUS || MP-WEIXIN path = file.tempFilePath // #endif } uni.uploadFile({ url: ossConfig.host, filePath: path, // 手机端文件路径 name: 'file', formData: { key: ossConfig.dir + name, // 文件名 policy: ossConfig.policy, // 后台获取超时时间 OSSAccessKeyId: ossConfig.ossAccessKeyId, // 后台获取临时ID success_action_status: 200, // 让服务端返回200,不然,默认会返回204 signature: ossConfig.signature // 后台获取签名 }, success(res) { resolve(`${ossConfig.host}/${ossConfig.dir}${name}`) }, complete(err) { console.log('err', err) } }); // #endif }) } export default { handleUpload }