copy 微信公众号文章复制到 summernote, 图片变成了 base64

更新日期: 2018-07-20 阅读次数: 11535 分类: 前端

问题

一个旅行社的客户反馈,从他们微信公众号复制之前的文章,粘贴到微信小程序的后台富文本编辑器 (summernote) 之后,点击保存出错。

从 laravel 日志看,是报了超出字段长度。从以往的经验看,如果使用了 base64 的图片才会有此问题。否则很难超过 text 类型 (TEXT 能存储 6.5 万个字符)

同事测试的时候,发现

  • 如果图片未加载完,复制到 summernote 里的就是 base64 图片
  • 如果图片加载完,复制到 summernote 里的就是图片的 URL 地址

这有点不合逻辑了。

排查

今天,我看看了微信公众号文章的网页代码,发现

<img class="img_loading" 
	data-ratio="0.75"
	data-s="300,640" 
	data-src="http://mmbiz.qpic.cn/mmbiz_jpg/fxIVaUUCtBNA247ibbOxCGnOE5SRzbYlpYibCNnfcTiagRTHYDpvNzJZXuxEjLxDfD2tI9Q33HcNEoPHm10tuCfRQ/640?wx_fmt=jpeg" 
	data-type="jpeg" 
	data-w="600" 
	width="auto" 
	_width="auto"						src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" 
	style="margin: 0px; padding: 0px; max-width: 100%; background-color: rgb(238, 237, 235); border-width: 1px; border-style: solid; border-color: rgb(238, 237, 235); background-size: 22px; background-position: center center; background-repeat: no-repeat; background-image: url("data:image/gif;base64,R0lGODlhPAA8APYAAJeXl56enp+fn6Cxxxxxxxxxx SO SO LONG xxxxxx

几点:

  1. data-src 属性里的才是图片的图片源地址
  2. src 当 class 为 img_loading 时,为 loading 的 base64 图片。细节处理的不错。(下面会说明此处理解的有问题)
  3. background-image 才是真正的变态大 base64 图片???

通过 https://codebeautify.org/base64-to-image-converter 转码之后,发现 background-image 就是一张 loading 的 gif 图片,而 src 里的 base64 并不是 loading 图片,是个空白的,我猜测是为了不导致 src 为空时的错误,所以找了个最小的 base64 图片。

这就真相大白了!

所谓的 base64 图片并不是原图,而是 loading 图片。

解决方案

看能否给 summernote 的 paste 加个 hook。

自动将 class 为 img_loading 的 img 做属性替换。

另外,加个字数统计,及限制。

summernote 的 paste hook

参考: https://summernote.org/deep-dive/#onpaste

// onPaste callback
$('#summernote').summernote({
  callbacks: {
    onPaste: function(e) {
      console.log('Called event paste');
    }
  }
});

替换之后,显示“此图片来自微信公众平台,未经许可不可引用”。

但是,为何加载完的图片,可以显示正常呢?

显示正常的图片

<img class="" data-ratio="0.75" data-s="300,640" data-src="http://mmbiz.qpic.cn/mmbiz_jpg/fxIVaUUCtBNA247ibbOxCGnOE5SRzbYlpYibCNnfcTiagRTHYDpvNzJZXuxEjLxDfD2tI9Q33HcNEoPHm10tuCfRQ/640?wx_fmt=jpeg" data-type="jpeg" data-w="600" width="auto" _width="auto" src="http://mmbiz.qpic.cn/mmbiz_jpg/fxIVaUUCtBNA247ibbOxCGnOE5SRzbYlpYibCNnfcTiagRTHYDpvNzJZXuxEjLxDfD2tI9Q33HcNEoPHm10tuCfRQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1" data-fail="0" style="margin: 0px; padding: 0px; max-width: 100%; height: auto !important; word-wrap: break-word !important; visibility: visible !important; width: auto !important;">

显示为盗链的图片

<img class="img_loading" data-ratio="0.6171875" data-s="300,640" data-src="http://mmbiz.qpic.cn/mmbiz_jpg/fxIVaUUCtBNA247ibbOxCGnOE5SRzbYlpsO04ZR3ia9vkncOU5Oz4hnKrjNefn7rWibJhuvicITPicaKOHicwibYa5EWA/640?wx_fmt=jpeg" data-type="jpeg" data-w="640" width="auto" _width="auto" src="http://mmbiz.qpic.cn/mmbiz_jpg/fxIVaUUCtBNA247ibbOxCGnOE5SRzbYlpsO04ZR3ia9vkncOU5Oz4hnKrjNefn7rWibJhuvicITPicaKOHicwibYa5EWA/640?wx_fmt=jpeg" style="margin: 0px; padding: 0px; max-width: 100%; background-color: rgb(238, 237, 235); border-width: 1px; border-style: solid; border-color: rgb(238, 237, 235); background-size: 22px; background-position: center center; background-repeat: no-repeat; height: 395px !important; word-wrap: break-word !important; visibility: visible !important; width: 640px !important;">

http://mmbiz.qpic.cn/mmbiz_jpg/fxIVaUUCtBNA247ibbOxCGnOE5SRzbYlpYibCNnfcTiagRTHYDpvNzJZXuxEjLxDfD2tI9Q33HcNEoPHm10tuCfRQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1 http://mmbiz.qpic.cn/mmbiz_jpg/fxIVaUUCtBNA247ibbOxCGnOE5SRzbYlpsO04ZR3ia9vkncOU5Oz4hnKrjNefn7rWibJhuvicITPicaKOHicwibYa5EWA/640?wx_fmt=jpeg

对比可见 src 部分是有区别的

&tp=webp&wxfrom=5&wx_lazy=1

这其实是错觉,因为可见图片是之前打开公众号时缓存到浏览器的,清除缓存之后,其实仍然不可见。

在微信小程序端,所有来自腾讯 cdn 的图片,都显示为空白,点击显示大图才可以看到。

使用自己的 CDN 存储

冷静分析了一下,感觉还是不要使用盗链比较稳妥。

  • 使用盗链增加了小程序的前端开发复杂度
  • 盗链方案哪天失效也未可知
  • 盗链图片是否一直存在,具有不确定性

这样实现的方案就变成了

  1. 解析出里面所有的微信 CDN 图片
  2. 逐一去掉 base64 属性,上传源地址,后台下载源图,上传到七牛 CDN,再返回七牛的地址

base64 是哪里引入的

这段 base64 loading 的代码是从这个 css 引入的:

var article_improve_combo_css = "//res.wx.qq.com/mmbizwap/en_US/htmledition/style/page/appmsg/page_mp_article_improve_combo3a7ab9.css";

通过 js 加载的。

实现代码

onPaste: function(e) {
      var sn = this;
      // 如果不适用 setTimeout 你会发现获取的是编辑之前的数据
      setTimeout(function () {
        // copy weixin 公众号
        $(e.currentTarget).find('img[data-src]').each(function() {
          var that = this;
          var img_src = $(this).data("src");
          if (img_src.indexOf('qpic.cn') == -1) {
            return;
          }

          // 上传图片源地址,并获取七牛 CDN 对应的图片地址
          $.ajax({
            type: "POST",
            url: "/upload_xxx",
            data: {
              wx_image: img_src
            },
            success: function(data) {
              if (data.err_code == 0) {
                $(that).replaceWith(function() {
                  return $('< img src="' + data.data + '">');
                });
                $(sn).summernote('triggerEvent', 'change');
              } else {
                alert("图片上传失败");
              }
            }
          });
        });
      }, 100);
    }

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式

谈笑风生

sup

你好,我对summernote刚接触,但是也碰到了这个微信公众号图片的问题,看完你的文章的解决方案后自己摸索实现,可惜还是无法实现,希望请你把这个自动将img_loading的img的属性替换的onPaste方法分享一下,如果不行也不勉强您。谢谢!

大象

代码不贴了,涉及到我们业务逻辑。其实也非常简单
1、解析出里面所有的微信 CDN 图片
2、逐一去掉 base64 属性,上传源地址,后台下载源图,上传到七牛 CDN,再返回七牛的地址

sup

可能我说的不清楚,我需要的不是涉及到业务逻辑的,就是你"自动将 class 为 img_loading 的 img 做属性替换。"这一部分,和显示“此图片来自微信公众平台,未经许可不可引用”。我需要的就是让它显示为"此图片来自微信工作平台,未经许可不可引用",后面的上传原地址,七牛CDN,我不需要,再次万分感谢

sup

其实我想要的代码可能你觉得很简单,就是一个javascript代码,功能就是将img替换属性,就这一块,但是我确实撸不出来,我是后台工程师,虽然这不是个理由,但我确实不会,泪奔。。。我觉得将Img替换属性这一个代码怎么都不会涉及你们业务逻辑啊,想不通,我想做到的,就是替换之后,显示“此图片来自微信公众平台,未经许可不可引用”。我不需要其他代码

大象

加我微信吧