mrr.sj.front/utils/albumUploadImage.js

299 lines
8.4 KiB
JavaScript
Raw Normal View History

2026-03-24 11:45:13 +08:00
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;
2026-03-25 13:29:04 +08:00
const isStandardHorizontal = standardRatio - actualRatio <= 0.334 || actualRatio - standardRatio <= tolerance;
2026-03-24 11:45:13 +08:00
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多端兼容APIAPP/小程序/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') {
2026-03-25 13:29:04 +08:00
//imageType = file.path.split('.')
imageType = file.name.split('.')
2026-03-24 11:45:13 +08:00
} 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
const ext = file.path.split('.').pop().toLowerCase()
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
}