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.mzph.cn/news/759091.shtml

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

相关文章

Qt笔记 事件处理_鼠标事件

什么是事件&#xff1f; 点击鼠标左键&#xff0c;双击鼠标左键&#xff0c;鼠标来回移动&#xff0c;按下键盘按钮&#xff0c;这些都是事件。 那么事件的响应机制是什么样的呢&#xff1f; 首先main函数中有一个QApplication&#xff0c;其作用是创建一个应用程序对象&…

【论文阅读】Masked Autoencoders Are Scalable Vision Learners

Masked Autoencoders Are Scalable Vision Learners 引用&#xff1a; He K, Chen X, Xie S, et al. Masked autoencoders are scalable vision learners[C]//Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2022: 16000-16009. 论文链…

UE4_官方动画内容示例1.3_ 运动混合空间(Locomotion BlendSpace)

如何使用运动&#xff08;Locomotion&#xff09;混合空间将Actor在不同方向上及不同速度的运动混合起来。&#xff08;例如&#xff0c;展示了一个混合了以不同速度向后、前、左和右走路/跑步动作的Actor&#xff09;。 一、相关知识点&#xff1a; 混合空间是允许根据多个输…

联想笔记本的声音键没有反应怎么办?

如果我的联想笔记本电脑上的声音按钮没有响应&#xff0c;该怎么办&#xff1f; 如果我的联想笔记本电脑上的声音按钮没有响应&#xff0c;该怎么办&#xff1f; 按下按钮后我无法控制声音。 我该怎么办&#xff1f; 以下是我为您整理的关于联想笔记本声音按键无反应的相关资料…

Android 10.0 app获取当前已连接wifi列表ssid和密码功能实现

1.前言 在10.0的系统定制化开发中,在一些关于wifi的定制中,有产品需求app中要求获取当前连接wifi和密码功能,在系统原生wifi中 是禁止获取wifi连接的密码的,所以就需要对wifi模块进行一部分的修改,来满足app中获取wifi的ssid和密码功能,接下来就来 实现这个功能 如图:…

金融知识分享系列之:支撑阻力

金融知识分享系列之&#xff1a;支撑阻力 一、支撑阻力原理二、支撑阻力作用1.识别市场资金的预期2.作为入场和平仓的重要参考 三、寻找支撑阻力四、延伸思考五、支撑阻力总结 一、支撑阻力原理 支撑阻力核心要素&#xff1a; 锚定效应订单驱动 支撑阻力原理&#xff1a; 市…

爬虫系列-CSS基础语法

&#x1f308;个人主页&#xff1a;会编程的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” CSS全称层叠样式表 &#xff0c;主要用来定义页面内容展示效果的一门语言&#xff0c;HTML&#xff1a;页面骨架&#xff0c;素颜CSS&#xff1a;页面效果美化&#xff1a…

01分布式搜索引擎ES

分布式搜索引擎ES 1.初识elasticsearch1.1.了解ES1.2.倒排索引1.3.es的一些概念 2.索引库操作2.1.mapping映射属性2.2.索引库的CRUD 3.文档操作3.1.新增文档3.2.查询文档3.3.删除文档3.4.修改文档3.5.总结 4.RestAPI4.0.导入Demo工程4.1.创建索引库4.2.删除索引库4.3.判断索引库…

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

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

java static

1、static概念 &#xff08;1&#xff09;static 关键字是静态的意思&#xff0c;可以修饰成员方法&#xff0c;成员变量 &#xff08;2&#xff09;static 修饰的特点 ① 被类的所有对象共享&#xff0c;这也是我们判断是否使用静态关键字的条件。 ② 可以通过类名调用&…

机器学习-06-回归算法

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中回归算法&#xff0c;包括线性回归&#xff0c;岭回归&#xff0c;逻辑回归等部分。 参考 fit_transform,fit,transform区别和作用详解&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&am…

ActiViz三维场景的基本要素——相机

文章目录 前言一、基本属性和方法二、相机操作三、高级功能四、 示例代码和应用五、总结前言 在ActiViz中,vtkCamera是一个非常重要的类,用于定义观察场景的视角和位置。作为三维可视化的核心组件之一,vtkCamera决定了用户在三维场景中所看到的图像内容和视角,因此它对于呈…

基于Java中的SSM框架实现在线通用旅游平台网站系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现在线通用旅游平台网站系统演示 摘要 近几年来&#xff0c;计算机网络的发展得到了飞速的提升&#xff0c;由此展开的一系列行业大洗牌也由此开始。早些年只是人们只是对于计算机和互联网有了些基础的认识&#xff0c;现在它正在悄悄的改变着我们生活的…

安装MySQL5.7.19 + 解决数据库乱码

文章目录 1.删除mysql服务 sc delete mysql2.解压到D:\mysql5.7下3.配置管理员环境变量4.D:\mysql5.7\mysql-5.7.19-winx64下创建my.ini1.创建文件2.文件内容 5.管理员打开cmd&#xff0c;切换到 D:\mysql5.7\mysql-5.7.19-winx64\bin6.输入 mysqld -install 安装mysql服务7.初…

网页无插件视频播放器,支持录像、截图、音视频播放,多路播放等,提供源码下载

前言 本播放器内部采用jessibuca插件接口&#xff0c;支持录像、截图、音视频播放等功能。播放器播放基于ws流&#xff0c;分屏操作支持1分屏、4分屏、6分屏、9分屏方式。 jessibuca工作原理是通过Emscripten将音视频解码库编译成Js&#xff08;WebAssembly&#xff0c;简称was…

Java集合Collection之LinkedList

LinkeList LinkedList&#xff08;双向链表&#xff09;是一种常见的线性数据结构&#xff0c;但是并不会按线性的顺序存储数据。它由一系列节点组成&#xff0c;每个节点包含数据部分和一个指向下一个节点的引用。相比于数组&#xff0c;链表具有动态大小、插入和删除效率高的…

【电气安全】ASCP电气防火限流式保护器/末端回路线路保护

为什么要使用电气防火限流式保护器&#xff1f; 应急管理部消防救援局统计&#xff0c;在造成电气火灾事故的原因中&#xff0c;最为主要的当为末端线路短路&#xff0c;在电气火灾事故中占比高达70%以上。如何效预防末端线路短路引发的电气火灾事故&#xff1f; 现阶段最为常…

T470 双电池机制

ThinkPad系列电脑牛黑科技双电池管理体系技术,你知道吗&#xff1f; - 北京正方康特联想电脑代理商 上文的地址 在放电情况下&#xff1a;优先让外置电池放电&#xff0c;当放到一定电量后开始让内置电池放电。 在充电情况下&#xff1a;优先给内置电池充电&#xff0c;当充…

航空实时监控

1、从Kafka中读取飞机数据&#xff0c;并进行清洗 此步骤在前面的“使用Spark清洗统计业务数据并保存到数据库中”任务阶段应该已经完成。如果没有完成&#xff0c;请参考源代码自行完成。核心类主要有三个&#xff1a;SparkStreamingApplication类、SparkUtil类和MapManager类…

目标检测——YOLOR算法解读

论文&#xff1a;YOLOR-You Only Learn One Representation: Unifified Network for Multiple Tasks 作者&#xff1a;Chien-Yao Wang, I-Hau Yeh, Hong-Yuan Mark Liao 链接&#xff1a;https://arxiv.org/abs/2105.04206 代码&#xff1a;https://github.com/WongKinYiu/yolo…