uniapp使用Canvas给图片加水印把临时文件上传到服务器

生成的临时路径是没有完整的路径没办法上传到服务器

16:37:40.993 添加水印后的路径,  _doc/uniapp_temp_1710923708347/canvas/17109238597881.png 
16:37:41.041 添加水印后的完整路径,  file://storage/emulated/0/Android/data/com.jingruan.zjd/apps/__UNI__BE4B000/doc/uniapp_temp_1710923708347/canvas/17109238597881.png

使用以下代码得到完整的路径

let path = 'file:/' + plus.io.convertLocalFileSystemURL(tempFilePath);


 

完整代码如下 使用的插件市场的hpy-watermark组件   一共2个

效果是

其他页面调用方式

<!-- 
增加水印上传villageReviewForm.preciseAddr 是通过高德获取的定位地址 
-->
<hpy-watermark ref="uploadImage" :address="villageReviewForm.preciseAddr" @waterMark="waterMark"></hpy-watermark>	

高德获取定位

		uni.getLocation({type: 'gcj02',geocode: true,isHighAccuracy: true,success: res => { const {province,city,district,street,streetNum,poiName} = res.address;this.villageReviewForm.preciseAddr =`${district}${street}${streetNum}${poiName}${res.longitude},${res.latitude}`;console.log("经纬度地点",this.villageReviewForm.preciseAddr)// 数据渲染this.$forceUpdate();}});

获取上传数据结果

const fileList = this.$refs.uploadImage.fileList

组件样式

hpy-watermark.vue

<template><view><view class="watermark-content"><canvas canvas-id="watermarkCanvas" id="watermarkCanvas" :style="{width:canvasWidth + 'px', height:canvasHeight + 'px'}"></canvas></view><upload-image   v-model="fileList" style="margin-left: 15rpx"   :image-styles="imageStyles" :files-list="filesList"   :delIcon="delIcon"   @choose="chooseImage" @delFile="delFile"><slot><view class="is-add"><view class="icon-add"></view><view class="icon-add rotate"></view></view></slot></upload-image></view>
</template><script>import {fileServerIp} from "@/common/utils/config.js"import Session from "@/common/Session";import uploadImage from './upload-image.vue'export default {components: {uploadImage, },name:'hpy-watermark',props:{address:{type:String,default:''},delIcon: {type: Boolean,default: true},listStyles: {type: Object,default () {return {// 是否显示边框border: true,// 是否显示分隔线dividline: true,// 线条样式borderStyle: {}}}},imageStyles: {type: Object,default () {return {width: 'auto',height: 'auto'}}},/*** 文字文字位置(默认:左下角)可选值:左上角:topLeft、右上角:topRight、左下角:bottomLeft、右下角:bottomRight*/markAlign:{type:String,default:function(){return 'bottomLeft'}},/*** 设置文本的水平对齐方式,默认:start,文本在指定的位置开始。* end	文本在指定的位置结束。* center 文本的中心被放置在指定的位置。* left	文本左对齐。* right	文本右对齐。*/textAlign:{type:String,default:function(){return 'start';}},/*** 设置文本的垂直对齐方式,默认:alphabetic文本基线是普通的字母基线。* top	文本基线是 em 方框的顶端。* hanging	文本基线是悬挂基线。* middle	文本基线是 em 方框的正中。* ideographic	文本基线是表意基线。* bottom	文本基线是 em 方框的底端。*/textBaseline:{type:String,default:function(){return 'alphabetic';}},/*** 文字大小*/fontSize:{type:[Number, String],default:30},/*** 文字颜色*/fontColor:{type:String,default:function(){return 'red'}},/*** 阴影颜色*/shadowColor:{type:String,default:function(){return 'rgba(0, 0, 0, 1.0)';}},/*** 阴影边框大小*/shadowWidth:{type:[Number, String],default:2},/*** 图片的质量,取值范围为 (0, 1],不在范围内时当作1处理*/quality:{type:[Number, String],default:1},/*** 目标文件的类型,只支持 'jpg' 或 'png'。默认为 'png'*/fileType:{type:String,default:function(){return 'png'}}},data() {return {fileList: [],files: [],filesList:[],canvasWidth:0,canvasHeight:0};},watch: { fileList: {handler(newVal, oldVal) { this.filesList=newVal;},immediate: true},},methods: {// 选择图片chooseImage() {if(this.isEmpty(this.address)){uni.showToast({icon:'none',title:'请打开定位或者重新获取'});return;}uni.chooseImage({count: this.limit,              // 限制的图片数量sizeType: ['compressed'],       // original 原图,compressed 压缩图,默认二者都有 sourceType: [ 'camera'],// album 从相册选图,camera 使用相机,默认二者都有success: (res) => {var imgPathList = res.tempFilePaths;if(imgPathList.length > 0){this.addImages(imgPathList);}},fail: (err) => {console.log('chooseImage fail', err)if("chooseImage:fail cancel" == err.errMsg){uni.showToast({icon:'none',title:'取消了选择'});}else{}}});},// 添加图片addImages(filePaths){if(filePaths.length > 0){var fillTexts = ["地址:"+this.address];fillTexts.push("时间:" + this.getNowTime());// 添加水印this.addWaterMark({filePaths,fillTexts});}},/*** 水印添加回调,在H5平台下,filePath 为 base64*/waterMark(filePath){this.imageList.push(filePath);},/*** 获取当前时间*/getNowTime(){var date = new Date(),year = date.getFullYear(),month = date.getMonth() + 1,day = date.getDate(),hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours(),minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(),second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();month >= 1 && month <= 9 ? (month = "0" + month) : "";day >= 0 && day <= 9 ? (day = "0" + day) : "";return (year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);},/*** 删除文件* @param {Object} index*/delFile(index) {this.$emit('delete', {tempFile: this.filesList[index],tempFilePath: this.filesList[index].url})this.filesList.splice(index, 1) },/*** 增加水印* @param {Object} {filePaths:['图片地址1', '图片地址2'], fillTexts:['水印1', '水印2']}*/async addWaterMark({ filePaths = [], fillTexts = [] }) {try{for (const filePath of filePaths) {await this.drawImage(filePath, fillTexts.reverse());}}catch(e){// TODO handle the exception}finally{// uni.hideLoading();}},/*** 绘制单个图片*/async drawImage(filePath, fillTexts){uni.showLoading({title:'图片处理中···'});const ctx = uni.createCanvasContext('watermarkCanvas', this);return new Promise(resolve => {uni.getImageInfo({src: filePath,success: (image) => {this.canvasWidth = image.width;this.canvasHeight = image.height;ctx.clearRect(0, 0, image.width, image.height);setTimeout(()=>{ctx.drawImage(image.path, 0, 0, image.width, image.height);ctx.setFontSize(this.fontSize);ctx.setFillStyle(this.fontColor);// 设置阴影let shadowWidth = Number(this.shadowWidth + "");if(shadowWidth > 0){ctx.shadowColor = this.shadowColor;ctx.shadowOffsetX = shadowWidth;ctx.shadowOffsetY = shadowWidth;}// 设置水平对齐方式ctx.textAlign = this.textAlign;// 设置垂直对齐方式ctx.textBaseline = this.textBaseline;const maxText = fillTexts.reduce((text, val) => {return text.length >= val.length ? text : val;});fillTexts.forEach((mark, index) => {if(this.markAlign == "bottomRight"){ctx.fillText(mark, image.width - (ctx.measureText(maxText).width+60), image.height - (index*60+60));}else if(this.markAlign == "topLeft"){ctx.fillText(mark, 20, (index*60+60));}else if(this.markAlign == "topRight"){ctx.fillText(mark, image.width - (ctx.measureText(maxText).width+60), (index*60+60));}else{ctx.fillText(mark, 20, image.height - (index*60+60));}});ctx.draw(false, (() => {setTimeout(()=>{uni.canvasToTempFilePath({canvasId: 'watermarkCanvas',fileType:this.fileType,quality:Number(this.quality + "" || "1"),success: (res) => { console.log("添加水印后的路径",res.tempFilePath )this.saveUploadImage(res.tempFilePath )},fail:(err) => {uni.hideLoading();console.log(err)},complete: () => {resolve();}}, this);}, 300);})());}, 200);},fail: (e) => {resolve();}});});},saveUploadImage(tempFilePath){uni.showLoading({title:'图片上传中···'});// #ifdef APP-PLUSvar p = plus.io.convertLocalFileSystemURL(tempFilePath);this.url = 'file:/' + pconsole.log("添加水印后的完整路径",this.url )// #endifuni.uploadFile({url: fileServerIp + 'common/upload',name: "file",// #ifdef H5filePath: tempFilePath,// #endif// #ifdef APP-PLUSfilePath: this.url,// #endifheader: {Authorization: "Bearer " + Session.getValue('token')},success: uploadFileRes => {uni.hideLoading();const {data} = JSON.parse(uploadFileRes.data)this.filesList.push({url: data.url,name: data.fileName,extname: 'png'}) this.$emit('waterMark',{url: data.url,name: data.fileName,extname: 'png'});},fail: error => {uni.hideLoading();uni.showToast({title: '上传失败!',icon: 'error',duration: 2000});}})}}}
</script><style scoped>.watermark-content{width: 0;height: 0;overflow: hidden;}.uni-file-picker__container {/* #ifndef APP-NVUE */display: flex;box-sizing: border-box;/* #endif */flex-wrap: wrap;margin: -5px;}.rotate {position: absolute;transform: rotate(90deg);}.icon-add {width: 50px;height: 5px;background-color: #f1f1f1;border-radius: 2px;}
</style>

upload-image.vue

<template><view class="uni-file-picker__container"><view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle"><view class="file-picker__box-content" :style="borderStyle"><image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image><view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)"><view class="icon-del"></view><view class="icon-del rotate"></view></view><view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress"><progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4":backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" /></view><view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)">点击重试</view></view></view><view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle"><view class="file-picker__box-content is-add" :style="borderStyle" @click="choose"><slot><view class="icon-add"></view><view class="icon-add rotate"></view></slot></view></view></view>
</template><script>export default {name: "uploadImage",emits:['uploadFiles','choose','delFile'],props: {filesList: {type: Array,default () {return []}},disabled:{type: Boolean,default: false},disablePreview: {type: Boolean,default: false},limit: {type: [Number, String],default: 9},imageStyles: {type: Object,default () {return {width: 'auto',height: 'auto',border: {}}}},delIcon: {type: Boolean,default: true},readonly:{type:Boolean,default:false}},computed: {styles() {let styles = {width: 'auto',height: 'auto',border: {}}return Object.assign(styles, this.imageStyles)},boxStyle() {const {width = 'auto',height = 'auto'} = this.styleslet obj = {}if (height === 'auto') {if (width !== 'auto') {obj.height = this.value2px(width)obj['padding-top'] = 0} else {obj.height = 0}} else {obj.height = this.value2px(height)obj['padding-top'] = 0}if (width === 'auto') {if (height !== 'auto') {obj.width = this.value2px(height)} else {obj.width = '33.3%'}} else {obj.width = this.value2px(width)}let classles = ''for(let i in obj){classles+= `${i}:${obj[i]};`}return classles},borderStyle() {let {border} = this.styleslet obj = {}const widthDefaultValue = 1const radiusDefaultValue = 3if (typeof border === 'boolean') {obj.border = border ? '1px #eee solid' : 'none'} else {let width = (border && border.width) || widthDefaultValuewidth = this.value2px(width)let radius = (border && border.radius) || radiusDefaultValueradius = this.value2px(radius)obj = {'border-width': width,'border-style': (border && border.style) || 'solid','border-color': (border && border.color) || '#eee','border-radius': radius}}let classles = ''for(let i in obj){classles+= `${i}:${obj[i]};`}return classles}},methods: {uploadFiles(item, index) {this.$emit("uploadFiles", item)},choose() {this.$emit("choose")},delFile(index) {this.$emit('delFile', index)},prviewImage(img, index) {let urls = []if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){this.$emit("choose")}if(this.disablePreview) returnthis.filesList.forEach(i => {urls.push(i.url)})uni.previewImage({urls: urls,current: index});},value2px(value) {if (typeof value === 'number') {value += 'px'} else {if (value.indexOf('%') === -1) {value = value.indexOf('px') !== -1 ? value : value + 'px'}}return value}}}
</script><style lang="scss">.uni-file-picker__container {/* #ifndef APP-NVUE */display: flex;box-sizing: border-box;/* #endif */flex-wrap: wrap;margin: -5px;}.file-picker__box {position: relative;// flex: 0 0 33.3%;width: 33.3%;height: 0;padding-top: 33.33%;/* #ifndef APP-NVUE */box-sizing: border-box;/* #endif */}.file-picker__box-content {position: absolute;top: 0;right: 0;bottom: 0;left: 0;margin: 5px;border: 1px #eee solid;border-radius: 5px;overflow: hidden;}.file-picker__progress {position: absolute;bottom: 0;left: 0;right: 0;/* border: 1px red solid; */z-index: 2;}.file-picker__progress-item {width: 100%;}.file-picker__mask {/* #ifndef APP-NVUE */display: flex;/* #endif */justify-content: center;align-items: center;position: absolute;right: 0;top: 0;bottom: 0;left: 0;color: #fff;font-size: 12px;background-color: rgba(0, 0, 0, 0.4);}.file-image {width: 100%;height: 100%;}.is-add {/* #ifndef APP-NVUE */display: flex;/* #endif */align-items: center;justify-content: center;}.icon-add {width: 50px;height: 5px;background-color: #f1f1f1;border-radius: 2px;}.rotate {position: absolute;transform: rotate(90deg);}.icon-del-box {/* #ifndef APP-NVUE */display: flex;/* #endif */align-items: center;justify-content: center;position: absolute;top: 3px;right: 3px;height: 26px;width: 26px;border-radius: 50%;background-color: rgba(0, 0, 0, 0.5);z-index: 2;transform: rotate(-45deg);}.icon-del {width: 15px;height: 2px;background-color: #fff;border-radius: 2px;}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/280479.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ES 常见面试题及答案

目录 es 写入数据流程 es 删除数据流程 es 读数据流程 es 部署的服务有哪些角色 es 的实现原理 es 和lucence 关系 如何提高写入效率 提高搜索效率 es doc value指的啥 分片指的啥&#xff0c;定义后可不可义再修改 深分页如何优化 对于聚合操作是如何优化的 元数据…

adobe animate 时间轴找不到编辑多个帧按钮

如题&#xff0c;找了半天&#xff0c;在时间轴上找不到编辑多个帧按钮,导致无法批量处理帧 然后搜索发现原来是有些版本被隐藏了&#xff0c;需要再设置一下 勾选上就好了

POI和EasyExcel区别和操作Excel

POI和EasyExcel操作Excel 常用场景 1、将用户信息导出为excel表格&#xff08;导出数据… &#xff09; 2、将Excel表中的信息录入到网站数据库&#xff08;文件数据上传… &#xff09; 开发中经常会设计到excel的处理&#xff0c;如导出Excel&#xff0c;导入Excel到数据库…

鸿蒙Harmony应用开发—ArkTS-转场动画(组件内隐式共享元素转场)

geometryTransition用于组件内隐式共享元素转场&#xff0c;在组件显示切换过程中提供平滑过渡效果。通用transition机制提供了opacity、scale等转场动效&#xff0c;geometryTransition通过id绑定in/out组件(in指入场组件、out指出场组件)&#xff0c;使得组件原本独立的trans…

Gateway新一代网关

Gateway新一代网关 1、概述 ​ Cloud全家桶中有个很重要的组件就是网关&#xff0c;在1.x版本中都是采用的Zuul网关&#xff1b; ​ 但在2.x版本中&#xff0c;zuul的升级一直跳票&#xff0c;SpringCloud最后自己研发了一个网关SpringCloud Gateway替代Zuul。 ​ 官网&…

手机运营商二要素检测:重塑信任基石,筑牢信息安全屏障

随着移动互联网的普及和数字经济的快速发展&#xff0c;用户信息安全的重要性日益凸显。运营商二要素检测作为一种强大的安全验证机制&#xff0c;以其精准匹配与实时验证的特性&#xff0c;为各类应用场景提供了一种可靠的身份识别解决方案&#xff0c;正在成为众多企业和服务…

C++:继承:面向对象编程的重要特性

(❁◡❁)(●◡●)╰(*▽*)╯(*/ω&#xff3c;*)(^///^)(❁◡❁)(❁◡❁)(●◡●)╰(*▽*)╯(*/ω&#xff3c;*)(❁◡❁)(●’◡’●)╰(▽)╯(/ω&#xff3c;)(///) C&#xff1a;继承&#xff1a;面向对象编程的重要特性 前言**继承**1.继承的概念及定义1.1继承的概念1.2继…

Redis6.0多线程的疑惑解答

1.Redis6.0之前的版本真的是单线程吗&#xff1f; Redis在处理客户端的请求是&#xff0c;包括获取(socket读)、解析、执行、内容返回(socket 写)等都有一个 顺序串行的主线程处理&#xff0c;这就是所谓的"单线程"。但如果严格来讲并不是单线程&#xff0c;除了主线…

SpringMVC学习笔记

SpringMVC 本篇笔记是基于尚硅谷学习资料的整理&#xff0c;涉及到其笔记的简化&#xff0c;补充&#xff0c;以及我在学习中遇到的与无法理解的问题及解决&#xff0c;如果想看完整及后续的笔记&#xff0c;可以去https://www.wolai.com/v5Kuct5ZtPeVBk4NBUGBWF查看官方笔记。…

演讲嘉宾公布 | 3D音频专题论坛将于3月27日举办

一、3D音频专题论坛 3D音频技术不仅能够提供更加真实、沉浸的虚拟世界体验&#xff0c;跨越时空的限制&#xff0c;探索未知的世界。同时&#xff0c;提供更加丰富、立体的情感表达和交流方式&#xff0c;让人类能够更加深入地理解彼此&#xff0c;建立更加紧密的联系。3D音频未…

MongoDB系列之查询计划

概述 一个查询具体如何被执行的过程&#xff0c;称为查询计划。MongoDB采用自底向上的方式来构造查询计划&#xff0c;每一个查询计划&#xff08;query plan&#xff09;都会被分解为若干个有层次的阶段&#xff08;stage&#xff09;。整个查询计划最终会呈现出一颗多叉树。…

Windows管理多版本nodejs方法

参考&#xff1a;window操作系统安装多个版本的nodejs——nodejs版本控制工具nvm_windows node多版本共存-CSDN博客 前排&#xff1a; 建议把原来电脑装的nodejs卸载干净&#xff0c;再干这个步骤&#xff0c;可以避免出现一些状况&#xff1b;&#xff1b;&#xff1b;另&…

免费PDF转换和编辑工具 PDFgear 2.1.4

PDFgear是一款功能强大的 PDF 阅读及转换软件。 它支持多种文件格式的转换和编辑&#xff0c;同时还提供了丰富的功能模块&#xff0c;如签名、表单填写等&#xff0c;方便用户进行多样化的操作。 该软件界面简洁美观&#xff0c;操作简单易懂&#xff0c;适合不同层次的用户…

【论文阅读】通过组件对齐评估和改进 text-to-SQL 的组合泛化

Measuring and Improving Compositional Generalization in Text-to-SQL via Component Alignment NAACL 2022| CCF B Abstract 在 text-to-SQL 任务中&#xff0c;正如在许多 NLP 中一样&#xff0c;组合泛化是一个重大挑战&#xff1a;神经网络在训练和测试分布不同的情况…

iostream、fstream、sstream、string、vector、unordered_map、stack

iostream 用于输入输出操作&#xff0c;包含了处理标准输入输出流的功能&#xff08;例如&#xff0c;cin, cout, cerr等&#xff09;。 #include <iostream>int main() {int number;std::cout << "Enter a number: ";std::cin >> number;std::…

基于Springboot的防疫物资管理信息系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的防疫物资管理信息系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

docker部署grafana graphite stastd实时监控告警配置_亲测成功

docker部署grafana graphite stastd实时监控告警配置_亲测成功 实时监控请求的次数和响应时间及异常报警 项目中需要监控API接口的调用情况,如:调用次数,每次调用耗时,调用高峰期,异常报警等 grafana graphite stastd这一套对代码没有侵入,也不会影响接口性能 安装docker环…

【C语言】【牛客】BC136 KiKi判断上三角矩阵

文章目录 题目 BC136 KiKi判断上三角矩阵思路代码呈现 题目 BC136 KiKi判断上三角矩阵 链接: link 思路 这题很简单但是再牛客中属于中等题 我们通过读题发现 2<n<10 &#xff0c;所以我们首先创建一个变量 n 以及一个 10*10 个元素数组 然后题目是判断该矩阵是否是…

Fetch、Axios 和 jQuery(Ajax) 三种常用的网络请求技术

Fetch、Axios 和 jQuery(Ajax) 是三种常用的网络请求技术&#xff0c;它们各自有着不同的特点和优势。本文将对这三种技术进行详细的介绍和比较&#xff0c;以帮助开发者更好地选择和使用合适的网络请求技术。 一、Fetch Fetch(浏览器自带) 是一种现代的网络请求 API&#xff…

强大的文本编辑器:Sublime Text for Mac注册激活版

Sublime Text for Mac是一款功能强大的文本编辑器&#xff0c;特别适合程序员和开发者使用。它提供了丰富的功能&#xff0c;如智能代码补全、语法高亮、自定义快捷键、项目管理、多行选择、自动保存等&#xff0c;以提高代码编写效率和舒适度。此外&#xff0c;Sublime Text还…