前言
在最近的一次需求中有这么一个需求想要我们可以复制图片,并且可以粘贴到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,[依赖项])