多图上传和水印都是比较难得,特别是有的api只支持在小程序用,h5不给用
效果图
普通的多图上传
// 多图上传
// count:最大数量
export function headerUploads0(count = 9, orderNumber = '', watermarkInfo) {
return new Promise((resolve, reject) => {
let UploadList = []
uni.chooseImage({
// 最多可以选择的图片总数
count,
success: async res => {
let processedFiles = res.tempFilePaths
if (watermarkInfo) {
// 创建列式水印文本数组
const watermarkLines = [
`订单号: ${watermarkInfo.number}`,
`买 家: ${watermarkInfo.traderName}`,
`卖 家: ${watermarkInfo.sellerTraderName}`,
`资金方: ${watermarkInfo.capitalTraderName}`,
`时 间: ${format(new Date(), false)}`
];
// 批量添加水印
}
//启动上传等待中...
uni.showLoading({
title: '上传中',
});
await Promise.all(
processedFiles.map((item, index) => {
return new Promise((resolve1, reject1) => {
console.log('上传');
uni.uploadFile({
// 上传地址
url: BASE_URL + VUE_APP_BASE_API +
'/AppBusiness/AppCommon/UploadFile',
name: 'file',
filePath: processedFiles[index],
formData: {
FileNameType: 3,
FileDir: orderNumber || '',
ClassifyType: orderNumber ? 'order' : ''
},
header: {
"Authorization": (store.state.token ||
uni.getStorageSync('token')) ?
'Bearer ' + (store.state.token ||
uni.getStorageSync('token')) :
''
},
success: (resz) => {
console.log('后端返回', (JSON.parse(resz
.data)
.data));
uni.hideLoading()
UploadList.push(JSON.parse(resz
.data)
.data)
resolve1()
},
fail: (resz) => {
console.log('失败返回', resz);
uni.hideLoading()
reject1()
}
})
});
}))
.then(() => {
console.log('循环后', UploadList);
resolve(UploadList)
})
.catch((error) => {
console.log('循环后', UploadList);
resolve(UploadList)
})
},
complete: compRes => {}
});
})
}
带水印的多图上传
在组件页面那需要加个空画布用来操作
// vue页面上
<!-- 隐藏的Canvas,用于绘制水印(全端兼容)-->
<canvas canvas-id="watermarkCanvas"
:style="{position: 'absolute', top: '0', left: '-1000vw', width: canvasWidth + 'px', height: canvasHeight + 'px'}"></canvas>
// 引入下面的函数tools.js是我的封装js,别搞错了
import {
headerUploads, // 多图上传
} from "@/utils/tools.js"
// data数据中
canvasWidth: 500, // 动态绑定Canvas宽高
canvasHeight: 500
// js中调用
// 参数 多少张图片3张,携带的订单号(不重要我这边上传后端要) Y12345, 订单信息 this.watermarkInfo, this 用来设置canvasWidth的
headerUploads(3, 'Y12345', this.watermarkInfo, this)
函数–封装的tools.js中
/**
* 跨平台图片压缩(兼容小程序和H5)
* @param {string} filePath 原始图片路径
* @param {number} [quality=0.7] 压缩质量(0-1)
* @param {number} [maxWidth=1024] 最大宽度
* @param {number} [maxHeight=1024] 最大高度
* @return {Promise<string>} 压缩后的图片路径
*/
export function compressImage(filePath, quality = 0.8, maxWidth = 1024, maxHeight = 1024) {
return new Promise((resolve, reject) => {
// 平台判断
// #ifdef MP-WEIXIN
// 微信小程序使用官方API
wx.compressImage({
src: filePath,
quality: Math.floor(quality * 100), // 微信使用0-100整数
success: (res) => resolve(res.tempFilePath),
fail: reject
});
// #endif
// #ifdef H5
// H5使用Canvas压缩
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = filePath;
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 计算压缩尺寸
let width = img.width;
let height = img.height;
if (width > maxWidth) {
height *= maxWidth / width;
width = maxWidth;
}
if (height > maxHeight) {
width *= maxHeight / height;
height = maxHeight;
}
canvas.width = width;
canvas.height = height;
// 绘制压缩图片
ctx.drawImage(img, 0, 0, width, height);
console.log(666);
// 获取压缩结果
canvas.toBlob(
(blob) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(blob);
},
'image/jpeg',
quality
);
};
img.onerror = reject;
// #endif
// 其他平台(如App)或未处理平台返回原图
// #ifndef MP-WEIXIN, H5
resolve(filePath);
// #endif
});
}
// 多图上传(带水印功能)
export function headerUploads(count = 9, orderNumber = '', watermarkInfo, pageContext) {
return new Promise((resolve, reject) => {
uni.chooseImage({
count,
success: async res => {
try {
const tempFiles = res.tempFilePaths;
let processedFiles = tempFiles;
// 如果需要加水印
if (watermarkInfo && watermarkInfo.number) {
const watermarkLines = [
`时 间: ${format(new Date(), false)}`,
`资金方: ${watermarkInfo.capitalTraderName || ''}`,
`卖 家: ${watermarkInfo.sellerTraderName || ''}`,
`买 家: ${watermarkInfo.traderName || ''}`,
`订单号: ${watermarkInfo.number}`
];
// 串行处理水印(确保画布状态独立)
processedFiles = [];
for (const imgPath of tempFiles) {
try {
const watermarked = await addWatermarkByContext(
imgPath,
watermarkLines,
pageContext
);
processedFiles.push(watermarked);
} catch (e) {
console.error('水印处理失败,使用原图:', e);
processedFiles.push(imgPath); // 失败时使用原图
}
}
}
// 上传所有处理后的文件
const UploadList = await uploadAllFiles(processedFiles, orderNumber);
resolve(UploadList);
} catch (e) {
reject(e);
}
},
fail: reject
});
});
}
// 重置画布状态(关键解决画布污染问题)
function resetCanvasContext(pageContext) {
if (!pageContext) return;
// 重置画布尺寸(避免上一张图片的尺寸影响)
pageContext.canvasWidth = 0;
pageContext.canvasHeight = 0;
// 清除画布内容
const ctx = uni.createCanvasContext('watermarkCanvas', pageContext);
ctx.clearRect(0, 0, pageContext.canvasWidth || 500, pageContext.canvasHeight || 500); // 清除超大区域确保干净
ctx.draw(); // 立即执行清除
}
// 具体的加水印
function addWatermarkByContext(imgPath, watermarkLines, pageContext) {
// 无需水印直接返回原图
if (!watermarkLines || watermarkLines.length === 0 || !pageContext) {
return Promise.resolve(imgPath);
}
return new Promise((resolve, reject) => {
// 1. 先重置画布状态(关键步骤)
resetCanvasContext(pageContext);
// 2. 获取原图信息
uni.getImageInfo({
src: imgPath,
success: (imgInfo) => {
const { width: imgWidth, height: imgHeight } = imgInfo;
const dpr = 1// uni.getSystemInfoSync().pixelRatio;
let dpr2 = uni.getSystemInfoSync().pixelRatio
// 3. 设置画布尺寸(使用原图尺寸)
pageContext.canvasWidth = imgWidth * dpr;
pageContext.canvasHeight = imgHeight * dpr;
const maxCanvasSize = 4096; // 大多数设备的限制
if (imgWidth * dpr > maxCanvasSize || imgHeight * dpr > maxCanvasSize) {
const scale = Math.min(maxCanvasSize/(imgWidth*dpr), maxCanvasSize/(imgHeight*dpr));
pageContext.canvasWidth = imgWidth * dpr * scale;
pageContext.canvasHeight = imgHeight * dpr * scale;
}
// 4. 创建画布上下文
const ctx = uni.createCanvasContext('watermarkCanvas', pageContext);
ctx.scale(dpr, dpr);
// 5. 绘制原图
ctx.drawImage(imgPath, 0, 0, imgWidth, imgHeight);
// 6. 绘制水印
const fontSize = 28 * dpr2;
const lineHeight = fontSize + 8;
const margin = 20 * dpr2;
ctx.setFontSize(fontSize);
ctx.setFillStyle('#3975e2');
ctx.setTextAlign('left');
ctx.setTextBaseline('bottom');
console.log(777);
watermarkLines.forEach((line, index) => {
const y = imgHeight - margin - (index * lineHeight);
ctx.fillText(line, margin, y);
});
// 7. 延迟确保绘制完成
setTimeout(() => {
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'watermarkCanvas',
destWidth: imgWidth,
destHeight: imgHeight,
fileType: 'jpg',
quality: 0.9,
success: (res) => {
// 8. 再次重置画布(为下一张图准备)
resetCanvasContext(pageContext);
resolve(res.tempFilePath);
},
fail: (err) => {
resetCanvasContext(pageContext);
reject(new Error(`Canvas转图片失败: ${JSON.stringify(err)}`));
}
}, pageContext);
});
}, 300); // 适当延迟确保绘制完成
},
fail: (err) => {
reject(new Error(`获取图片信息失败: ${JSON.stringify(err)}`));
}
});
});
}