多圖上傳和水印都是比較難得,特別是有的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.tempFilePathsif (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// 微信小程序使用官方APIwx.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, H5resolve(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)}`));}});});
}