复制内容和复制图片

292 阅读3分钟

前言

在最近的一次需求中有这么一个需求想要我们可以复制图片,并且可以粘贴到IM端,或者富文本编辑器中,在刚开始的开发中我也是按照正常的复制方式使用,在各类编辑器中都可以进行粘贴使用,但当我向IM端中粘贴时,发现有的IM端对我粘贴的内容拦截了,然后询问后才知道有的IM端为了防范XSS攻击,只允许粘贴text格式的内容进来,而我先前所做的为富文本编辑的形式,直接复制了html到此之间,导致用户并不能正常的粘贴复制的图片到端,所以特此写下这篇文章做记录

复制为内容时

 因为复制内容也是这次需求的部分内容,所以也在这里做了些总结

 原生的形式进行复制

    navigator.clipboard.writeText(内容)

 借助三方工具来进行

    //首先下包 npm i clipboard
    //其次举例以react为主
    useEffect(()=>{
        //成功的回调
        const copySuccessHandle=(e:ClipboardJs.Event) =>{
            message.success("copy success")
        }
        //失败的回调
        const copyFailedHandle=()=>{
            try{
            //使用原生方式进行复制
            navigator?.clipboard.writeText(内容).then(()=>{message.success("copy success")}).catch(()=>{message.error("copy failed")})
            }catch(error){
                message.error("copy failed")
            }
        }
        //这里输出复制的实例,绑定触发的元素和复制的内容,然后绑定成功和失败的事件
        const clipboard = new ClipboardJs("#copyNButton",{text:copyValue})
        clipboard.on("success",copySuccessHandle)
        clipboard.on("errror",copyFailedHandle)
       return()=>{
       //因为是类绑定的事件,最终要卸载一下防止内存泄漏,解除所有事件绑定
       clipboard.destroy()
           
           }
    },[copyValue])

复制为图片时

这里我不得不先提示一下,clipboard这个东西,必须在https或者起本地服务时才可以使用,浏览器防止一些不安全的事情做的防护

 先举例我复制为html格式时的处理

const copyImg = useDebounce(
    async () => {
//判断图片链接存在
    if (src) {
        try {
          const img = new Image();
          img.src = src;
          img.style.width = '200px';
          img.style.height = '200px';
//等待图片渲染结束
          await imgload(img);
//创建一个元素
          const dom = document.createElement('div');
          dom.appendChild(img);
//设置img元素内容可被编辑
          img.setAttribute('contenteditable', 'true');
          document.body.appendChild(img);
//获取原生的选中和创建范围
          const selection = window.getSelection();
          const range = document.createRange();
//范围选中图片
        range.selectNode(img);
//选中先清除原有选中内容在添加该内容
          selection?.removeAllRanges();
          selection?.addRange(range);
//原生的调用copy方法然后将图片设置为不可编辑,然后清除所有被使用的元素防止内存泄漏
          document.execCommand('copy');
          img.setAttribute('contenteditable', 'false');
          document.body.removeChild(img);
          dom.remove();
              message.success(t('share.copy_success'));
        } catch (error) {
          console.error(error);
        }
      }
    },
    30,
    [qrcodeSrc, t],
  );

 这里是复制为文本格式进行的

这里需要注意的是火狐不存在ClipboardItem这个实例,如果有想兼容的需要自己再研究研究,我这里并没有做特殊处理

    //注意防抖哈,不然一直调用影响性能
   const copyImage = useDebounce(()=>{
   if(!imgSrc){
       message.error("image is not exist");
       return;
   }
   try{
       //先控制必须是https协议
        if (!window.location.protocol.startsWith('https')) {
                        message.error(t('share.please_use_https'));
            return;
          }
         //我这里是canvas绘制的图片转换为数据流的形式,各位姐妹直接是图片的话用fetch请求下图片就可以了哈
             canvasRef.current?.toBlob(async(blob)=>{
             if(!blob){
                 message.error("copy image failed");
                 return;
             }
             //将数据流写入复制板子项之中,注意这玩意必须接收为流

await (window.navigator.clipboard as any).write([
              new ((window as any)?.ClipboardItem as any)({
                [blob.type]: blob,
              }),
            ]);
            message.success(t('share.copy_success'));
          }, 'image/png');
        } catch (error) {
          message.error(t('share.copy_failed'));
        }
      } else {
        message.error(t('share.copy_failed'));
      }
    },
200,[依赖项])