Maison  >  Article  >  interface Web  >  Savez-vous comment le front-end implémente le filigrane ?

Savez-vous comment le front-end implémente le filigrane ?

醉折花枝作酒筹
醉折花枝作酒筹avant
2021-07-30 16:57:012627parcourir

Dans le travail quotidien, nous rencontrons souvent de nombreuses données sensibles. Afin d'éviter les fuites de données, nous devons effectuer un certain « packaging » sur les données. L'objectif est d'amener les « éléments criminels » intéressés par la fuite de données à abandonner leur comportement illégal sous la forte « pression de l'opinion publique » et à en faire des « tentatives criminelles » pour obtenir l'effet de vaincre les autres sans se battre.

Ceux d'entre nous qui travaillent dans le service de sécurité, le concept de sécurité des données est déjà profondément ancré dans nos os. Chaque texte et chaque image doivent tenir compte de l'existence ou non d'un risque de fuite de données. question à laquelle nous avons réfléchi. Par exemple, le filigrane des images est un problème que nous rencontrons souvent dans notre processus de travail. Étant donné que le contenu de mon travail est le développement de la plateforme d'évaluation, certaines images risquées apparaissent souvent sur la plateforme d'évaluation. Étant donné que la sensibilisation à la sécurité des évaluateurs est inégale, afin d'éviter que des choses dangereuses ne se produisent, il est nécessaire d'ajouter des filigranes. les photos de.

Analyse du problème

Tout d'abord, compte tenu du scénario commercial, le problème à ce stade est simplement de s'inquiéter de la fuite de données lors du processus de révision. Pour le moment, nous ne considérons que les filigranes explicites, ce qui signifie l'ajout de texte ou autre. des mots à l’image qui peuvent distinguer vos données d’identité personnelle. De cette manière, les individus peuvent être retrouvés sur la base des données divulguées. Bien entendu, sa fonction la plus importante est de prendre des précautions et de prévenir les problèmes avant qu'ils ne surviennent.

Résoudre le problème

Méthodes de mise en œuvre

Il existe de nombreuses façons de mettre en œuvre les filigranes Selon la division du personnel qui met en œuvre les fonctions, elles peuvent être divisées en filigranes front-end et back-end. Les avantages des filigranes frontaux peuvent être résumés en trois points. Premièrement, ils n'occupent pas les ressources du serveur et s'appuient entièrement sur la puissance de calcul du client, réduisant ainsi la pression sur le serveur. Deuxièmement, c'est rapide, quelle que soit l'implémentation frontale implémentée, les performances sont meilleures que celles du back-end. Troisièmement, la mise en œuvre est simple. Le plus grand avantage de la mise en œuvre du filigrane sur le backend peut également être résumé en trois points, à savoir la sécurité, la sécurité et la sécurité. Zhihu et Weibo utilisent tous deux des solutions de filigrane back-end. Cependant, après un examen approfondi, nous adoptons toujours la solution frontale pour mettre en œuvre le filigrane. Ce qui suit présentera également brièvement comment nodejs implémente le filigrane d'image back-end.

node Implementation

fournit trois packages npm. Cette partie n'est pas l'objet de notre article, seule une simple démo est fournie.

1, gm https://github.com/aheckmann/gm 6,4k star

const 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');
});

Nécessite l'installation de GraphicsMagick ou ImageMagick

2, node-images : https://github.com/zhangyuanwei/node- ; images

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(&#39;div&#39;);
        // 动态设置偏移值
        waterDom.setAttribute(&#39;style&#39;, `
            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 = &#39;测试水印&#39;;
        wrap.appendChild(waterDom);
    }
}

Pas besoin d'installer d'autres outils, légers, développés par zhangyuanwei chinois, documentation chinoise ;

3, jimp : https://github.com/oliver-moran/jimp

Peut être utilisé avec gifwrap pour réaliser un gif filigrane ;

Implémentation frontale

1, l'image d'arrière-plan implémente un filigrane plein écran

Vous pouvez vérifier l'effet sur la page d'informations personnelles à l'intérieur et à l'extérieur d'Alibaba

Avantages : L'image est générée par le backend, en toute sécurité ;

Inconvénient : Nécessité de lancer une requête http pour obtenir des informations sur l'image ;

Affichage de l'effet : Puisqu'il s'agit d'un système interne, il n'est pas pratique d'afficher l'effet.

2. DOM implémente le filigrane d'image complet et le filigrane d'image

Obtenez la largeur et la hauteur de l'image dans l'événement de chargement de l'image, générez la zone de filigrane en fonction de la taille de l'image et bloquez-la sur la couche supérieure de l'image. Le contenu DOM est la copie du filigrane ou d'autres informations. Comparaison des méthodes de mise en œuvre Simple.

const wrap = document.querySelector(&#39;#ReactApp&#39;);
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(&#39;p&#39;);
        // 动态设置偏移值
        waterDom.setAttribute(&#39;style&#39;, `
            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 = &#39;测试水印&#39;;
        wrap.appendChild(waterDom);
    }
}

Avantages : simple et facile à mettre en œuvre ;

Inconvénients : des images trop grandes ou trop nombreuses affecteront les performances

3, méthode d'implémentation du canevas (plan d'implémentation de la première version)

Méthode 1 : opérer directement sur l'image

Sans plus tarder, passons directement au code

useEffect(() => {
      // gif 图不支持
    if (src && src.includes(&#39;.gif&#39;)) {
      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 = &#39;hanging&#39;;
        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(&#39;2d&#39;);
        context.drawImage(this, 0, 0);
        context.rect(0, 0, image.width || 200, image.height || 200);
           // 设置水印浮层
        const pattern = context.createPattern(innerCanvas, &#39;repeat&#39;);
        context.fillStyle = pattern;
        context.fill();
      } catch (err) {
        console.info(err);
        setShowImg(true);
      }
    };
    image.onerror = function () {
      setShowImg(true);
    };
  }, [src]);

Avantages : implémentation frontale pure, les images copiées par un clic droit sont également filigranées ;

Inconvénients : le GIF n'est pas pris en charge, les images doivent prendre en charge plusieurs domaines ;

Affichage des effets : donné ci-dessous.

Méthode 2 : Le canevas génère une URL de filigrane et l'attribue à l'attribut d'arrière-plan CSS

export const getBase64Background = (props) => {
  const { nick, empId } = GlobalConfig.userInfo;
  const {
    rotate = -20,
    height = 75,
    width = 85,
    text = `${nick}-${empId}`,
    fontSize = &#39;14px&#39;,
    lineWidth = 2,
    fontFamily = &#39;microsoft yahei&#39;,
    strokeStyle = &#39;rgba(255, 255, 255, .15)&#39;,
    fillStyle = &#39;rgba(0, 0, 0, 0.15)&#39;,
    position = { x: 30, y: 30 },
  } = props;
  const image = new Image();
  image.crossOrigin = &#39;Anonymous&#39;;
  const canvas = document.createElement(&#39;canvas&#39;);
  const context = canvas.getContext(&#39;2d&#39;);
  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 = &#39;center&#39;;
  context.textBaseline = &#39;hanging&#39;;
  context.strokeText(text, position.x, position.y);
  context.fillText(text, position.x, position.y);
  return canvas.toDataURL(&#39;image/png&#39;);
};

// 使用方式 
<img src="https://xxx.xxx.jpg" />
<p className="warter-mark-area" style={{ backgroundImage: `url(${getBase64Background({})})` }} />

Avantages : Implémentation frontale pure, prend en charge plusieurs domaines, prend en charge les filigranes d'image git ;

Inconvénients : L'URL base64 générée est relativement grand ;

En fait, sur la base de ces deux Pour la première méthode de mise en œuvre du canevas, vous pouvez facilement proposer la troisième méthode, qui consiste à recouvrir la couche supérieure de l'image avec un canevas sans image dans la première méthode , de sorte que les inconvénients des deux méthodes puissent être parfaitement évités. Mais arrêtez-vous un instant et réfléchissez-y. Existe-t-il un moyen plus simple et plus simple de combiner les deux solutions et d'utiliser la toile pour dessiner ? Oui, utilisez plutôt svg.

4, la méthode SVG (la solution actuellement utilisée)

donne une version réactive du composant filigrane.

export const WaterMark = (props) => {
  // 获取水印数据
  const { nick, empId } = GlobalConfig.userInfo;
  const boxRef = React.createRef();
  const [waterMarkStyle, setWaterMarkStyle] = useState(&#39;180px 120px&#39;);
  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=\&#39;100%\&#39; height=\&#39;100%\&#39; xmlns=\&#39;http://www.w3.org/2000/svg\&#39; version=\&#39;1.1\&#39;><text width=\&#39;100%\&#39; height=\&#39;100%\&#39; x=\&#39;20\&#39; y=\&#39;68\&#39;  transform=\&#39;rotate(-20)\&#39; fill=\&#39;rgba(0, 0, 0, 0.2)\&#39; font-size=\&#39;14\&#39; stroke=\&#39;rgba(255, 255, 255, .2)\&#39; stroke-width=\&#39;1\&#39;>${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(&#39;px&#39;, &#39;&#39;)) < 180) {
      setWaterMarkStyle(`${width} ${height.replace(&#39;px&#39;, &#39;&#39;) / 2}px`);
    };
    previousSibling.style.height = height;
    previousSibling.style.width = width;
    previousSibling.style.top = `${offsetTop}px`;
    previousSibling.style.left = `${offsetLeft}px`;
    // 加载 loading 隐藏
    nextSibling.style.display = &#39;none&#39;;
  };
  const onError = (event) => {
    setIsError(true);
  };
  return (
    <p className={styles.water_mark_wrapper} ref={boxRef}>
      <p className={styles.water_mark_box} style={boxStyle} />
      {isError
        ? <ErrorSourceData src={src} showSrc={showSrc} height={propsHeight} text="图片加载错误" helpText="点击复制图片链接" />
        : (
          <>
            <img onLoad={onLoad} referrerPolicy="no-referrer" onError={onError} src={src} alt="图片显示错误" />
            <Icon className={styles.img_loading} type="loading" />
          </>
        )
      }
    </p>
  );
};

Avantages : prend en charge les filigranes gif, pas de problèmes inter-domaines, utilise l'attribut de répétition, pas d'insertion dans le processus dom, pas de problèmes de performances

QA

Question 1 :

Si vous supprimez le dom de ; le filigrane D'accord, l'image n'a-t-elle pas de filigrane ?

Réponse :

Vous pouvez utiliser MutationObserver pour surveiller le nœud d'eau. Si le nœud est modifié, l'image sera masquée ;

Question 2 :

Cliquez avec le bouton droit de la souris pour copier l'image ;

Réponse :

La fonction clic droit est désactivée pour toutes les images

Question 3 :

Et si les informations sur l'image sont obtenues à partir du réseau de la console ?

Réponse :

Il n'existe actuellement aucune bonne solution pour cette opération. Il est recommandé d'utiliser une solution d'implémentation back-end

.

Résumé

La solution de filigrane mise en œuvre sur le front-end n'est toujours qu'une solution temporaire, et la mise en œuvre du back-end métier consomme des ressources de serveur. En fait, la solution la plus idéale est de fournir un service de filigrane indépendant, même s'il y en aura. un léger retard dans le processus de chargement. Par rapport à la sécurité des données, des retards de l'ordre de la milliseconde sont toujours acceptables, ce qui peut garantir que la stabilité du service de l'entreprise n'est pas affectée.

Au cours du processus quotidien de questions-réponses, de nombreuses parties commerciales viendront également vers moi pour communiquer avec moi sur les points de risque d'occlusion des filigranes. Chaque fois, je ne peux leur répondre qu'en soulignant l'importance de la sécurité des données. Bien sûr, la taille et la transparence. , et densité du filigrane Ils sont également optimisés en permanence, et je pense qu'il y aura une version qui pourra non seulement fonctionner comme un filigrane, mais aussi mieux résoudre le problème d'occlusion.

Apprentissage recommandé : Tutoriel vidéo HTML

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer