当前位置: 首页 > 后端技术 > Node.js

下面说说实现水印的几种方法

时间:2023-04-03 16:59:34 Node.js

遇到的问题在日常工作中,我们经常会遇到很多敏感数据。为了防止数据泄露,我们需要对数据做一些“包装”。目的是让那些有意泄露数据的“不法分子”在严峻的“舆论压力”下放弃违法行为,使其“作案未遂”,达到不战而胜之效果。对于我们这些在安全部门工作的人来说,数据安全的概念早已深入骨髓。一定要注意每一个文字和图片是否存在泄露风险。如何防止数据泄露是我们一直在思考的问题。比如图片的水印,就是我们工作中经常涉及到的问题。因为工作内容本身就是审计平台的开发,审计平台上经常会出现一些有风险的图片。考虑到审核人员的安全意识参差不齐,为防止不安全的事情发生,需要给图片加上水印。的。分析问题首先考虑业务场景,现阶段的问题只是担心审核过程中数据泄露。我们暂时只考虑显式水印,即在图片上添加一些文字或其他可以区分您个人身份的数据。这样就可以根据泄露的数据追查个人。当然,未雨绸缪、防患于未然的预警功能是最重要的。有很多方法可以解决这个问题。有很多方法可以实现水印。按照功能分工,可以分为前端水印和后端水印。前端水印的优点可以概括为三点。第一,不占用服务器资源,完全依赖于客户端。计算能力,减轻服务器压力。二是速度快,无论是哪种前端实现方式,性能都优于后端。第三,实现方法简单。在后台实现水印最大的优势也可以概括为三点,即安全、安全、安全。知乎和微博都使用了后台实现的水印方案。但综合考虑后,我们还是采用了前端加水印的方案。下面就简单介绍一下nodejs是如何实现后端图片水印的。node实现提供了三个npm包,这部分不是我们本文的重点,只提供一个简单的demo。1、gmhttps://github.com/aheckmann/gm6.4kstarconstfs=require('fs');constgm=require('gm');gm('/path/to/my/img.jpg').drawText(30,20,"GMagick!").write("/path/to/drawing.png",function(err){if(!err)console.log('完成');});需要安装GraphicsMagick或ImageMagick;2、node-images:https://github.com/zhangyuanwei/node-imagesvarimages=require("images");images("input.jpg")//从文件加载图片//loadImagefile.size(400)//将图像几何缩放到400像素宽//将图像缩放到400像素宽.draw(images("logo.png"),10,10)//在坐标(10,10)处绘制徽标//DrawtheLogoat(10,10).save("output.jpg",{//保存图片到文件,质量为50quality:50//保存图片到文件,image质量为50});无需安装其他工具,轻量级,zhangyuanwei中文开发,中文文档;3、跳转:https://github.com/oliver-moran/jimp可以配合gifwrap实现gif水印;前端实现1,背景图实现全屏水印。可以去阿里内外的个人信息页面查看效果。原理:优点:图片由后台生成,安全;缺点:需要发起http请求,获取图片信息;效果展示:因为是内部系统,不方便展示效果2、dom在图片的onload事件中实现全图水印和图片水印,获取图片的宽高,并根据生成水印区域到图片的大小,覆盖图片的上层,dom的内容就是水印的文案或者其他信息,实现方法比较简单。constwrap=document.querySelector('#ReactApp');const{clientWidth,clientHeight}=wrap;constwaterHeight=120;constwaterWidth=180;//计数const[columns,rows]=[~~(clientWidth/waterWidth),~~(clientHeight/waterHeight)]for(leti=0;i{//gif图片不支持if(src&&src.includes('.gif')){setShowImg(true);}图像e.onload=function(){try{//太小的图不加载水印if(image.width<10){setIsDataError(true);props.setIsDataError&&props.setIsDataError(true);返回;}constcanvas=canvasRef.current;canvas.width=image.width;canvas.height=image.height;//设置水印constfont=`${Math.min(Math.max(Math.floor(innerCanvas.width/14),14),48)}px`||字体大小;innerContext.font=`${font}${fontFamily}`;innerContext.textBaseline='悬挂';innerContext.rotate(旋转*Math.PI/180);innerContext.lineWidth=lineWidth;innerContext.strokeStyle=strokeStyle;innerContext.strokeText(文本,0,innerCanvas.height/4*3);innerContext.fillStyle=fillStyle;innerContext.fillText(文本,0,innerCanvas.height/4*3);constcontext=canvas.getContext('2d');context.drawImage(this,0,0);Context.rect(0,0,image.width||200,image.height||200);//设置水印浮层constpattern=context.createPattern(innerCanvas,'repeat');context.fillStyle=模式;上下文.fill();}catch(err){console.info(err);setShowImg(真);}};image.onerror=function(){setShowImg(true);};},[来源]);优点:纯前端实现,右键复制的图片也带水印;缺点:不支持gif,图片必须支持跨域;效果展示:下图方法二:canvas生成水印url赋值给css背景属性exportconstgetBase64Background=(props)=>{const{nick,empId}=GlobalConfig.userInfo;const{rotate=-20,height=75,width=85,text=`${nick}-${empId}`,fontSize='14px',lineWidth=2,fontFamily='微软雅黑',strokeStyle='rgba(255,255,255,.15)',fillStyle='rgba(0,0,0,0.15)',position={x:30,y:30},}=props;常量图像=新图像();image.crossOrigin='匿名';constcanvas=document.createElement('canvas');constcontext=canvas.getContext('2d');canvas.width=宽度;canvas.height=高度;context.font=`${fontSize}${fontFamily}`;context.lineWidth=lineWidth;context.rotate(旋转*Math.PI/180);context.strokeStyle=strokeStyle;context.fillStyle=fillStyle;context.textAlign='center';context.textBaseline='悬挂';context.strokeText(text,position.x,position.y);context.fillText(文本,po位置.x,位置.y);returncanvas.toDataURL('image/png');};//如何使用优点:纯前端实现,支持跨域,支持git图片水印;缺点:生成的base64url??比较大;效果展示:下面给出。其实根据这两个canvas的实现方法,你很容易想出第三种方法,就是在图片的上层覆盖一层非图片canvas,这样就可以完美了。避免了这两种方案的缺点。但是先停下来想一想,把两种方案结合起来,或者用canvas来画,有没有更简单易懂的方式呢?是的,改用svg。4.SVG方法(正在使用的解决方案)提供水印组件的反应版本。exportconstWaterMark=(props)=>{//获取水印数据const{nick,empId}=GlobalConfig.userInfo;constboxRef=React.createRef();const[waterMarkStyle,setWaterMarkStyle]=useState('180px120px');const[isError,setIsError]=useState(false);const{src,text=`${nick}-${empId}`,height:propsHeight,showSr??c,img,nick,empId}=道具;//设置背景图片和背景图片样式constboxStyle={backgroundSize:waterMarkStyle,backgroundImage:`url("data:image/svg+xml;utf8,${text}")`,};constonLoad=(e)=>{constdom=e.目标;const{previousSibling,nextSibling,offsetLeft,offsetTop,}=dom;//获取图片的宽高const{width,高度}=getComputedStyle(dom);if(parseInt(width.replace('px',''))<180){setWaterMarkStyle(`${width}${height.replace('px','')/2}px`);};previousSibling.style.height=height;previousSibling.style.width=宽度;previousSibling.style.top=`${offsetTop}px`;previousSibling.style.left=`${offsetLeft}px`;//加载加载隐藏nextSibling.style.display='none';};constonError=(event)=>{setIsError(true);};返回({isError?:(<>)}

);};优点:支持gif图片水印,无跨域问题,使用repeat属性,无dom插入过程,无性能问题;缺点:。.Dom结构展示:5、效果图展示canvas和svg实现的效果在展示上差别不大,所以效果图都是一张图展示。QA问题1:如果水印的dom被删除,图片不是没有水印吗?答:您可以使用MutationObserver来监控水节点。如果节点被修改,图片也会被隐藏;问题2:用鼠标右键复制图片?答:所有图片都禁用了右键功能。问题三:如果主机的图片信息是从网络获取的怎么办?答:这个操作暂时没有想到好的解决方案。推荐采用后端实现方案。前端实现的水印方案永远只是临时方案,业务的后端实现会消耗服务器资源。其实最理想的方案是提供一个独立的水印服务,虽然在加载过程中会有轻微的延迟,但是相对于数据安全来说,毫秒级的延迟还是可以接受的,从而保证服务的稳定性的业务不会受到影响。在日常的问答过程中,会有很多业务方来找我交流关于水印阻断风险点的问题。我只能每次都以数据安全的重要性来回复他们。当然,水印的大小、透明度、密度也不同。在不断的调优中,相信会有一个版本,既能起到水印的作用,又能更好的解决遮挡问题。作者:ES2049/布鲁文章可随意转载,但请保留原文链接。如果你有热情,非常欢迎你加入ES2049Studio。请将简历发送至caijun.hcj@alibaba-inc.com。