捣鼓TinyMCE粘贴图片并上传+Flask后台

好久没有编程了,最近需要完成一个小功能,为了方便,需要粘贴图片后上传到后台。前台编辑器用tinymce(N年前用过,我也就知道这个编辑器而已。这次使用下来感觉文档更丰富了),后台我用的Flask。昨天从下午4点开始一直捣鼓到半夜2点,终于完成了,这里大致记录一下遇到的问题和解决的办法。

1.  使用的tinymce版本是4.7.4,稍微熟悉了一下,advlist 插件不能用。 粘贴图片主要用的是自带的paste插件。

 tinymce.init({
        selector:'#mycontent',
        menubar:false,
        plugins: [
            'code paste',
		],
        toolbar:'code',
        height:400,
        paste_data_images: true
});

2. 上面的代码已经能成功粘贴图片,并且显示在编辑框里面了。我查看了源代码,图片的src是这样的

<img src="blob:http://127.0.0.1:5000/e8b7743b-6637-45f5-8473-9cf5271cc841">

3. 我懵逼了,这是什么玩意儿,但是似乎听说过Blob这个东西。我又想了下我的思路,我是要把这个图像上传到后台,然后把后台的地址返回给客户端,然后把这个src给替换掉。 因此我的问题就是把这个图片读出来,然后上传。一开始我网上找到不少解决方法,可以把blob读出来,主要是用了HTML5的canvas 对象。用context 画图,然后输出DataURL,转化为Base64。 这一段耗费了我大量的时间。 下面的代码是有问题的,贴出来仅仅是为了表达完整的过程。

var Img = new Image();
Img.src=url;  
width = Img.width; // 实际上是拿不到长宽的。
height = Img.height; var canvas = document.createElement("canvas"); canvas.getContext("2d").drawImage(Img,0,0, width, height); dataURL=canvas.toDataURL('image/jpeg');

 大致的核心代码如上。rul: 就是上面 blob:http://.... 这一段。关键在于drawImage这个函数上,我为了看看这个画布上画出来的图像是否跟我粘贴的图像是一样的,因此在页面下部让这个画布显示出来,我遇到了几个问题

   1. 第一次粘贴,画布无法显示图像,第二次粘贴后,画布会一次显示2个图像,而且错误 -__-!

   2. 画布大小和图像大小不匹配。 打印了width和height 之后发现都是0。原来blob图像无法获得长宽。

   3. 我粘贴的图像比较大,1800×3600以上,Base64太长了,传到后台,保存成图像之后,数据丢失,出来的是一片白色。

  因此这条路基本上是走不通了。

4. 解决方法:换成纯二进制数据进行操作,不再转换。

直接看下面的代码吧,写的很粗,因为我不是很懂javascript。基本上全是复制粘贴过来的。

<script>
    
    globalcounter = 1;
   
    tinymce.init({
        selector:'#mycontent',
        menubar:false,
        plugins: [
            'code paste',
			],
        toolbar:'code',
        height:400,
        paste_data_images: true,
        paste_preprocess: function(plugin, args) {                  
            args.content = args.content.replace("<img", "<img id="pasted_image_" + parseInt(globalcounter) + """);
            console.log(args.content)
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){
                if (this.readyState == 4 && this.status == 200){
              
                    upload(this.response);
                }
            };

            xhr.open('GET', args.content.split('"')[3]);
            xhr.responseType = 'blob';
            xhr.send(); 

            function upload(BlobFile){
                var x = new XMLHttpRequest();
                x.onreadystatechange = function(){
                    if( this.readyState == 4 && this.status == 200 ){
                        data = this.responseText;
                        console.log('response data: ' + data);
                        id = parseInt(globalcounter++);

                        // function setimg(id, data){
                        //     if( document.getElementById("pasted_image_" + id)  == null){
                        //         setTimeout( setimg , 5000);                                
                        //     }else{
                        //         document.getElementById("pasted_image_" + id).setAttribute("src", data);      
                        //     }
                        // }
                        document.getElementById("mycontent_ifr").contentWindow.document.getElementById("pasted_image_" + id).setAttribute("src", data);
                    }
                };
                x.open('POST', '/pasteimg/');   
                x.send(BlobFile);
            }

        }
        
    });
</script>  

这里是后台的python代码,框架用的是Flask

@app.route('/pasteimg/', methods=['GET','POST'])
def paste_upload():
    if request.method == 'POST':
        imgdata = request.get_data()
        file = open('test.png', 'wb')
        file.write(imgdata)
        file.close()

    imgsrc = "/static/img/60_1.png"
    return Response( imgsrc,  mimetype='application/text')

  

上面的代码有几个我遇到的问题,解决了。也有一些问题,我只是绕过了,但是没有真正解决;:

1. 用XMLHttpRequest 可以直接把img 读出来成为一段二进制数据 -- blob。而且没有大小限制,我贴的图片有时候size超过7位数。。也能够顺利上传到后台,相比之下,我觉得比Base64一串常常的字符串好多了。

2. tinymce 的paste_postprocess 中无法给插入对象设置id,这样我之后就没有办法获取图片了,因此,我只能在preprocess中设置了id,由于要插入多张图片,因此我用一个全局的计数器来递增id号。以后如果有多个textarea,那么还要再想办法。

3. jquery 的ajax方法不能传二进制,因此用XMLHttpRequest.

4. XMLHttpRequest 不能设置同步操作,设置了之后contenttype就不能设置blob,否则出错。我没时间去处理了,就用异步了。

5. 由于服务器端我命名规则还没有订好,因此我只是返回固定的一个图片,这样只要在前端能够顺利显示,说明整个路子都走通了。

6. 获取图片对象费了老大劲,原来tinymce自己是一个iframe,因此要先找到iframe,然后再找到里面的图片。 在chrome里面我直接用getElementById(”图片的id“)却始终找不到,奇怪。 后来我到Firefox中就能找到了。

7. 当服务器端把图像返回回来时,如果paste事件还没有完成,那么DOM对象还没有生成的话,图片对象是找不到的。虽然这种可能性很小,所以我设置了timeout,找不到就一直给我找,直到找到位置。当然也可以用一个wait 的gif,类似 博客园这么干。

8. 我的性子是不求甚解,能拼凑就拼凑,能猜就猜。。。。反正捣鼓出来就行了,什么代码优化之类的与我无缘。 只是我用了好多关键字搜索,但是搜出来的解决方案都有问题,所以我才写了这篇文章,希望也能帮助看文章的你节省宝贵的时间。

原文地址:https://www.cnblogs.com/moogle/p/8079610.html