把公众号的文章同步发布到博客
解决微信公众号图片外链失效问题
一键复制文章到剪切板并把图片转存到博客服务器(不使用公众号图片外链也不使用cdn)
从公众号复制文章,粘贴到博客编辑器后
会变成下图所示,提示未经允许不可引用
参考文章
《WordPress自定义插件:代码高亮,允许内联样式,显示隐藏按钮》
想了以下几种方法
一.使用chrome插件
-
使用chrome插件自带的API,获取剪切板的内容,并把图片处理
-
使用ffmpeg.wasm把图片压缩和转码为webp格式
-
使用当前活动窗口网站的cookie,调用wordpress rest api上传图片到媒体库
-
把文章中图片的地址替换为最终的图片地址
遇到的问题
除了cookie以外,wordpress还需要nonce参数 (防止 WordPress 网站受到 CSRF 攻击)
这个nonce暂时没有方法从网站获取到,所以chrome插件排查
二.wordpress插件
如果是把图片存在cdn,那么不需要wordpress插件就可以通过js,python或者api等解决
但是要把剪切板中图片url或者base64,blob图片转存到wordpress,那么使用wordpress更为方便
直接写方法即可,不用考虑登录,权限,后期备份,迁移等问题
只需要安装插件即可
具体思路
-
监听wordpress中tinymce编辑器的复制粘贴事件
-
在复制前对剪切板中的数据进行图片查询,符合条件的将进行压缩和转码以及上传处理
-
图片处理后把成功的url进行替换
首先,在tinymce初始化中新加参数
add_filter('tiny_mce_before_init', 'custom_tinymce_config');
设置允许复制图片
$init['paste_data_images'] = "true";
设置粘贴后处理
此选项使您能够在将粘贴的内容插入编辑器之前但在将其解析为 DOM 结构之后对其进行修改
官网文档
https://www.tiny.cloud/docs/plugins/opensource/paste/
要特别注意:
官网文档演示的是JS的环境,但是本文是wordpress中php的环境
函数的声明是php中的字符串
$init['paste_postprocess'] ="function (plugin, args) {}"
php错误演示
以下代码是我最初的写法,都是不对的,只有上面的代码才是正确的
function test(){
}
$init['paste_postprocess'] ="test"
// or
$init['paste_postprocess'] =test
为什么不选则粘贴预处理
因为tinymce函数返回的参数args.content是字符串html,很难对指定标签进行查找或者过滤
而paste_postprocess中args.node是dom形式的nodelist,可以直接设置dom属性来改变样式和属性
paste_preprocess
查到复制内容dom中所有的img标签
使用querySelectorAll来查找img标签集合
let imgArr = args.node.querySelectorAll('img')
对集合做遍历
对每一个img标签设置id和图片加载错误事件监听
-
生成一个随机字符串作为img标签id,方便后面定位
-
把图片的源地址(如公众号图片地址)作为自定义属性放在标签上(data-origin)
-
把图片地址改为即将存放在博客服务器的地址(域名+文件夹+随机字符串+图片后缀)
-
设置图片加载错误监听,只要监听到错误,就把图片id放在window全局变量中(errImgArr)(暂时不对图片处理,后面统一按顺序进行队列处理,避免同时上传图片)
js生成随机字符串
function randomString(length) {
var str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
var result = '';
for (var i = length; i > 0; --i) {
result += str[Math.floor(Math.random() * str.length)];
}
return result;
}
设置图片自定义
let imgArr = args.node.querySelectorAll('img')
if (imgArr && imgArr.length > 0) {
window.errImgArr=[]
for (let i = 0; i < imgArr.length; ++i) {
let ranStr = randomString(10)
ranStrArr[i] = ranStr
imgArr[i].ranStr = ranStr
imgArr[i].imgUrl = imgArr[i].getAttribute('src')
imgArr[i].setAttribute('data-origin', imgArr[i].getAttribute('src'))
imgArr[i].setAttribute('alt', `img`)
imgArr[i].setAttribute('id', ranStr)
imgArr[i].setAttribute('src', host+`/wp-content/uploads/images/`+ranStr+`.png`)
imgArr[i].setAttribute('data-src', host+`/wp-content/uploads/images/`+ranStr+`.png`)
imgArr[i].onerror =(e)=>{
console.log(`图片加载错误`,e,window)
window.errImgArr.push(imgArr[i].ranStr)
}
}
}
设置延时3到5秒后,查看window全局变量errImgArr
如果有值,则使用for循环同步并按顺序上传和设置正确图片地址
通过tinyMCE.activeEditor.contentDocument方法可以获取tinymce编辑器中的document
直接使用document获取的是博客管理后台的dom,非编辑器的dom
async function imgtoggle(){
if(window&&window.errImgArr&&window.errImgArr.length>0){
for (var i = 0; i<window.errImgArr.length ; i++) {
let domAll=tinyMCE.activeEditor.contentDocument
let imgDom=domAll.getElementById(window.errImgArr[i])
let srcUrl=imgDom.getAttribute('data-origin')
let idStr=imgDom.getAttribute('id')
imgDom.setAttribute('src',host+`/wp-content/uploads/images/`+`loading.gif`)
try{
console.log(`开始`,i,`图片上传`,srcUrl,idStr)
let imgRes=await saveImg(srcUrl,idStr)
imgDom.setAttribute('src',imgRes.imgDomainUrl)
imgDom.setAttribute('data-src',imgRes.imgDomainUrl)
}catch(err){
}
}
}
}
setTimeout(()=>{
imgtoggle()
},5000)
其中saveImg为图片上传到博客的php接口
saveImg函数
设置接口为同级目录的api.php
传递参数为原图片地址,和图片id(作为图片文件名)
注意这里使用post方法和formdata传参,是为了避免get参数过长和特殊字符问题
使用formdata方便php简单取值
function saveImg(imgUrl, ranStr) {
var host = document.location.origin
return new Promise((resolve, reject) => {
let formdata = new FormData();
formdata.append('imgUrl', imgUrl);
formdata.append('ranStr', ranStr);
const options = {
method: 'POST',
body: formdata,
};
fetch(host + '/wp-content/plugins/tinymceSet/' + 'api.php', options)
.then(data => {
return data.json();
})
.then(res => {
console.log('res', res);
if (res && res.imgDomainUrl) {
resolve(res)
}
})
})
}
新建api.php在插件目录
使用$_POST[‘xxx’]获取参数
使用file_put_contents和file_get_contents开保存图片文件
// 获取要转存的图片url
$url=$_POST['imgUrl'];
// 设置图片服务器存储地址
$rand_str=$_POST['ranStr'];
$img = '../../uploads/images/';
$img .=$rand_str;
$img .='.png';
// 存储图片
file_put_contents($img, file_get_contents($url));
// 获取图片实际的域名地址
$domain = $_SERVER['HTTP_HOST'];
$imgDomainUrl='//';
$imgDomainUrl.=$domain;
$imgDomainUrl.='/wp-content/uploads/images/';
$imgDomainUrl .=$rand_str;
$imgDomainUrl .='.png';
$arr = array('code' => 0, 'data' => $_POST["imgUrl"], 'msg' => 'success','domain' => $domain,'imgDomainUrl' => $imgDomainUrl );
echo json_encode($arr);
注意,要想是api.php正常保存图片,图片目录的权限要设置正确
755权限无反应,777权限可以正常写入
到此为止
从公众号复制文章内容,然后复制到博客tinymce(经典编辑器)
可以完全复制内容,并且图片自动保存到博客并展示(不会出现外链不可使用等问题)
为了插件兼容别的博客,作为域名动态处理,上传的地址会匹配当前博客地址
var host = document.location.origin
写这个插件预期解决以下问题
-
避免每次公众号文章同步到博客,因为图片问题,需要画10-30分钟来做复制编辑,设置插件后,只需要1-3分钟,文章越多,节省的时间越多
-
为后续自动化压缩图片做准备,一篇文章图片多了,访问速度相对会下降,且占据磁盘空间,后续可以使用js,php或者单独的api对图片自动压缩
-
为后续图片转码做准备,jpg,png,gif等转为webp格式,尺寸小,seo友好
END