怎麼實現浮水印?淺析水印實現的幾種方式
怎麼實現浮水印?這篇文章給大家分享幾種浮水印實現的方式,有需要的朋友可以學習了解一下~
遇到問題
#在日常工作中,經常會遇到很多敏感的數據,為防止數據的洩露,我們要在數據上做一些」包裝「。目的就是讓那些有心洩露資料的」不法分子「迫於嚴重的」輿論壓力「而放棄不法行為,使之」犯罪未遂“,達到不戰而屈人之兵的效果。而在安全部門工作的我們,資料安全的觀念早已深入骨髓,每個文字,每張圖片,都要留心是否有洩露的風險,怎麼防止資料洩露,是我們一直在思考的問題。例如圖片的浮水印,就是我們工作過程中常涉及的問題。因為本身工作內容就是審核平台的開發,經常有一些風險圖片會在審核平台出現,考慮到審核人員的安全意識參差不齊,所以為防止不安全的事情發生,圖片增加水印的工作是必須要做的。
分析問題
首先,考慮到業務場景,現階段的問題只是在審核過程中擔心資料的洩露,我們暫時只考慮顯式水印,既在圖片上增加一些可以區別你個人身份的文字或其他資料。這樣就可以做到根據洩漏的資料可以追蹤到個人,當然,未雨綢繆,防患於未然的警示功能才是它最主要。
解決問題
實現方式
#水印的實作方式有很多,根據實作功能的人員分工可以分為前端浮水印和後端浮水印,前端浮水印的優點可以總結為三點,第一,可以不佔用伺服器資源,完全依賴客戶端的運算能力,減少服務端壓力。第二,速度快,無論哪一種前端的實作方式,效能都是優於後端的。第三,實作方式簡單。後端實現水印的最大優勢也可以總結為三點,就是安全,安全,安全。知乎,微博都是採用後端實現的水印方案。但綜合考慮,我們還是採用前端實現浮水印的方案。下面也會簡單介紹下 nodejs 怎麼實作後端圖片浮水印。node實作
提供三個 npm 包,本部分不是我們文章的重點,只提供簡單的 demo。1,gm
https://github.com/aheckmann/gm 6.4k starconst fs = require('fs'); const gm = require('gm'); gm('/path/to/my/img.jpg') .drawText(30, 20, "GMagick!") .write("/path/to/drawing.png", function (err) { if (!err) console.log('done'); });
node-images
:https://github.com/zhangyuanwei/node-imagesvar images = require("images"); images("input.jpg") //Load image from file //加载图像文件 .size(400) //Geometric scaling the image to 400 pixels width //等比缩放图像到400像素宽 .draw(images("logo.png"), 10, 10) //Drawn logo at coordinates (10,10) //在(10,10)处绘制Logo .save("output.jpg", { //Save the image to a file, with the quality of 50 quality : 50 //保存图片到文件,图片质量为50 });

:https://github.com/oliver-moran/jimp
可搭配gifwrap 實現gif 水印;
前端實現1,背景圖實作全螢幕浮水印
可以到阿里內外個人資訊頁面查看效果,原理:
優點:圖片是後端生成,安全性;
缺點:需要發起http 請求,取得圖片資訊;效果顯示:由於是內部系統,不方便展示效果。
2,dom 實作全圖浮水印和圖片浮水印
在圖片的onload 事件裡取得圖片寬高,根據圖片大小產生浮水印區域,遮擋在圖片上層,dom 內容為水印的文案或其他訊息,實現方式比較簡單。
const wrap = document.querySelector('#ReactApp'); const { clientWidth, clientHeight } = wrap; const waterHeight = 120; const waterWidth = 180; // 计算个数 const [columns, rows] = [~~(clientWidth / waterWidth), ~~(clientHeight / waterHeight)] for (let i = 0; i < columns; i++) { for (let j = 0; j <= rows; j++) { const waterDom = document.createElement('div'); // 动态设置偏移值 waterDom.setAttribute('style', ` width: ${waterWidth}px; height: ${waterHeight}px; left: ${waterWidth + (i - 1) * waterWidth + 10}px; top: ${waterHeight + (j - 1) * waterHeight + 10}px; color: #000; position: absolute` ); waterDom.innerText = '测试水印'; wrap.appendChild(waterDom); } }
優點:簡單易實作;
缺點:圖片過大或過多會有效能影響;
效果顯示:
3,canvas 實作方式(第一版實作方案)
方法一:直接在圖片上操作
廢話不多說,直接上程式碼useEffect(() => { // gif 图不支持 if (src && src.includes('.gif')) { setShowImg(true); } image.onload = function () { try { // 太小的图不加载水印 if (image.width < 10) { setIsDataError(true); props.setIsDataError && props.setIsDataError(true); return; } const canvas = canvasRef.current; canvas.width = image.width; canvas.height = image.height; // 设置水印 const font = `${Math.min(Math.max(Math.floor(innerCanvas.width / 14), 14), 48)}px` || fontSize; innerContext.font = `${font} ${fontFamily}`; innerContext.textBaseline = 'hanging'; innerContext.rotate(rotate * Math.PI / 180); innerContext.lineWidth = lineWidth; innerContext.strokeStyle = strokeStyle; innerContext.strokeText(text, 0, innerCanvas.height / 4 * 3); innerContext.fillStyle = fillStyle; innerContext.fillText(text, 0, innerCanvas.height / 4 * 3); const context = canvas.getContext('2d'); context.drawImage(this, 0, 0); context.rect(0, 0, image.width || 200, image.height || 200); // 设置水印浮层 const pattern = context.createPattern(innerCanvas, 'repeat'); context.fillStyle = pattern; context.fill(); } catch (err) { console.info(err); setShowImg(true); } }; image.onerror = function () { setShowImg(true); }; }, [src]);
export const getBase64Background = (props) => { const { nick, empId } = GlobalConfig.userInfo; const { rotate = -20, height = 75, width = 85, text = `${nick}-${empId}`, fontSize = '14px', lineWidth = 2, fontFamily = 'microsoft yahei', strokeStyle = 'rgba(255, 255, 255, .15)', fillStyle = 'rgba(0, 0, 0, 0.15)', position = { x: 30, y: 30 }, } = props; const image = new Image(); image.crossOrigin = 'Anonymous'; const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.width = width; canvas.height = height; context.font = `${fontSize} ${fontFamily}`; context.lineWidth = lineWidth; context.rotate(rotate * Math.PI / 180); context.strokeStyle = strokeStyle; context.fillStyle = fillStyle; context.textAlign = 'center'; context.textBaseline = 'hanging'; context.strokeText(text, position.x, position.y); context.fillText(text, position.x, position.y); return canvas.toDataURL('image/png'); }; // 使用方式 <img src="/static/imghw/default1.png" data-src="https://xxx.xxx.jpg" class="lazy" / alt="怎麼實現浮水印?淺析水印實現的幾種方式" > <div className="warter-mark-area" style={{ backgroundImage: `url(${getBase64Background({})})` }} />
export const WaterMark = (props) => { // 获取水印数据 const { nick, empId } = GlobalConfig.userInfo; const boxRef = React.createRef(); const [waterMarkStyle, setWaterMarkStyle] = useState('180px 120px'); const [isError, setIsError] = useState(false); const { src, text = `${nick}-${empId}`, height: propsHeight, showSrc, img, nick, empId } = props; // 设置背景图和背景图样式 const boxStyle = { backgroundSize: waterMarkStyle, backgroundImage: `url("data:image/svg+xml;utf8,<svg width=\'100%\' height=\'100%\' xmlns=\'http://www.w3.org/2000/svg\' version=\'1.1\'><text width=\'100%\' height=\'100%\' x=\'20\' y=\'68\' transform=\'rotate(-20)\' fill=\'rgba(0, 0, 0, 0.2)\' font-size=\'14\' stroke=\'rgba(255, 255, 255, .2)\' stroke-width=\'1\'>${text}</text></svg>")`, }; const onLoad = (e) => { const dom = e.target; const { previousSibling, nextSibling, offsetLeft, offsetTop, } = dom; // 获取图片宽高 const { width, height } = getComputedStyle(dom); if (parseInt(width.replace('px', '')) < 180) { setWaterMarkStyle(`${width} ${height.replace('px', '') / 2}px`); }; previousSibling.style.height = height; previousSibling.style.width = width; previousSibling.style.top = `${offsetTop}px`; previousSibling.style.left = `${offsetLeft}px`; // 加载 loading 隐藏 nextSibling.style.display = 'none'; }; const onError = (event) => { setIsError(true); }; return ( <div className={styles.water_mark_wrapper} ref={boxRef}> <div className={styles.water_mark_box} style={boxStyle} /> {isError ? <ErrorSourceData src={src} showSrc={showSrc} height={propsHeight} text="图片加载错误" helpText="点击复制图片链接" /> : ( <> <img src="/static/imghw/default1.png" data-src="https://img.php.cn/upload/image/323/319/107/1629255185289626.png" class="lazy" onLoad={onLoad} referrerPolicy="no-referrer" onError={onError} src={src} alt="图片显示错误" /> <Icon className={styles.img_loading} type="loading" /> </> ) } </div> ); };
优点:支持 gif 图水印,不存在跨域问题,使用 repeat 属性,无插入 dom 过程,无性能问题;
缺点:。。。
dom 结构展示:
5,效果图展示
canvas 和 svg 实现的效果在展示上没有很大的区别,所以效果图就一张图全部展示了。
QA
问题一:
如果把 watermark 的 dom 删除了,图片不就是无水印了吗?
答案:
可以利用 MutationObserver 监听 water 的节点,如果节点被修改,图片也随之隐藏;
问题二:
鼠标右键复制图片?
答案:
全部的图片都禁用了右键功能
问题三:
如果从控制台的network获取图片信息呢?
答案:
此操作暂时没有想到好的解决办法,建议采用后端实现方案
总结
前端实现的水印方案始终只是一种临时方案,业务后端实现又耗费服务器资源,其实最理想的解决方式就是提供一个独立的水印服务,虽然加载过程中会略有延迟,但是相对与数据安全来说,毫秒级的延迟还是可以接受的,这样又能保证不影响业务的服务稳定性。
在每天的答疑过程中,也会有很多业务方来找我沟通水印遮挡风险点的问题,每次只能用数据安全的重要性来回复他们,当然,水印的大小,透明度,密集程度也都在不断的调优中,相信会有一个版本,既能起到水印的作用,也能更好的解决遮挡问题。
原文地址:https://segmentfault.com/a/1190000040425430
作者:ES2049 /卜露
更多编程相关知识,请访问:编程视频!!
以上是怎麼實現浮水印?淺析水印實現的幾種方式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

想知道美圖秀怎麼加浮水印嗎?美圖秀秀是一款非常好用的修圖p圖軟體,提供摳圖放在另一個圖、改圖片大小kb、去浮水印、換證件照底色、全螢幕加時間日期地點水印等功能,可以幫助使用者快速的完成圖片的製作。有用戶製作了自己的圖片不想被別人盜圖想要鋪滿自己的浮水印,但不知道如何操作?小編現在為大家分享美圖秀加入浮水印方法!喜歡就快來下載吧!一、美圖秀怎麼加浮水印?分享美圖秀加入浮水印方法! 1.開啟在本站下載的美圖秀2023版。美圖秀2023版類別:拍攝美化下載美圖秀2023版是一款功能豐富的圖片美化與編輯軟

去除浮水印是軟體掃描全能王中的一個好用的工具,有些用戶還不太清楚掃描全能王去除水印怎麼去除,可以在保存界面點擊編輯PDF中的去除水印即可閉,接下來就是小編為用戶帶來的去除浮水印方法的介紹,有興趣的用戶快來一起看看吧!掃描全能王使用教學掃描全能王去除浮水印怎麼去除答:可以在儲存介面點選編輯PDF中的移除浮水印詳情介紹:1、進入軟體,點選【相機】圖示。 2、拍攝掃描需要去浮水印的文件。 3.點選【→】進入下一步。 4.完成編輯後,點選【✓】。 5.點選其中的【編輯PDF】。 6.選擇下方的【去除浮水印】即可。

Wink如何去視訊浮水印?winkAPP中是有去除掉視訊浮水印的工具,但是多數的小伙伴不知道wink中如何去除掉視頻中的水印,接下來就是小編為玩家帶來的Wink視頻去水印方法圖文教程,有興趣的用戶快來一起看看吧! Wink如何去視訊浮水印1、先開啟winkAPP,在首頁面專區中選擇【去浮水印】功能;2、然後在相簿中選擇你需要去除水印的影片;3、接著選擇影片之後,剪輯影片之後點擊右上角【√】;4、最後點選如下圖的【一鍵去印】之後點選【處理】即可。

小米14Ultra是一款性能配置非常不錯的全新機型,這款手機還採用了與眾不同的外觀設計,顏值更高辨識度也很不錯,很多入手的消費者們想要了解一下小米14Ultra怎麼去除浮水印? ,下面本站小編就來為大家介紹吧!小米14Ultra怎麼去除浮水印? 1.開啟相機應用程式:找到並開啟小米14預先安裝的相機應用程式。 2.進入設定選單:點選畫面右下角或左下角(取決於您使用哪個版本)顯示其他選項按鈕。然後,在彈出式選單中選擇“設定”。 3.關閉浮水印選項:在設定選單中,您將看到各種選項和參數。向下滑動直到找到“浮水印”選項

開啟剪映並選擇模板,編輯好影片以後點選匯出,點選無浮水印並分享。教學適用型號:iPhone13系統:iOS15.3.1版本:剪映6.8.0解析1開啟剪映並選擇剪輯範本。 2點選開啟模板後,點選右下角的剪同款選項。 3從相簿中選擇照片填充片段並點擊下一步。 4如何點選頁面右上角的匯出選項。 5最後點選無浮水印並分享就可以了。補充:剪映是什麼軟體1剪映是影片編輯軟體,擁有全面的剪輯功能,可以變速、有濾鏡和美顏效果,還有豐富的曲庫資源。自2021年2月起,剪映支援在手機行動端,Pad端,Mac電

為了讓拍攝出的照片更具個性和獨特性,小米14提供了拍照水印設定。透過設定拍照浮水印,使用者可以在拍攝的照片上添加圖案、文字和標誌,使得每一張照片都能更好地記錄下珍貴的時刻和回憶。接下來,我們將介紹如何在小米14中設定拍照浮水印,讓您的照片更加個人化和生動。小米14怎麼設定拍照浮水印? 1.首先點選“相機”。 2、然後點選「設定」。 3.接著找到浮水印,隨後就可以開始拍攝了。

win1124H2如何消除右下角的評估副本文字?我們在使用系統的時候有時候桌面會在右下角的螢幕顯示一個透明的浮水印,那麼這個透明的水印要怎麼去掉?使用者可以直接的利用第三方軟體來進行操作就可以了。以下就讓本站來為使用者來仔細的介紹一下消除win1124H2評估副本浮水印的方法吧。消除win1124H2評估副本浮水印的方法下載UniversalWatermarkDisabler工具,運行後會顯示出目前系統的版本以及水印狀態,「Status」中顯示「Readyforinstallation」即已可以移除

1.我們用WPS打開一篇文檔,裡面有浮水印,看起來比較亂,怎麼去掉呢?往下看。 2.選單列找到插入選項卡,選擇此選項卡底下的頁首和頁尾圖標,用滑鼠左鍵在上面點擊一下。 3.這時頁面文字變成灰色的不可編輯狀態,但是此時文字背面的浮水印就可以編輯了。 4.點擊浮水印,可以看到這是圖片浮水印,因為在這張圖片周圍出現幾個小方塊,此時圖片為可以編輯狀態。 5.用鍵盤delete鍵刪除圖片,可以看到浮水印不見了。 6.頁面雙擊滑鼠,退出頁眉頁腳編輯模式,頁面文字又變成正常色可以編輯狀態,但此時頁面上的浮水印已經不見了。 7、
