美文网首页Flask实践
基于Editor.MD的Flask图片实现

基于Editor.MD的Flask图片实现

作者: hallucigenia | 来源:发表于2018-11-25 21:21 被阅读3次

    Editor.MD的Flask图片上传实战

    inital

    继上篇 基于Flask的Markdown编辑器实践选择的Editor.MD为博客提供的MarkDown编辑器自带图片上传接口,如果不使用Flask-Uploads的话也是很简便的。这篇相当于上篇的一个补充和拓展。

    在html里添加这三行:imageUploadURL里填后面用的上传路由

    <script type="text/javascript">
        $(function () {
            editormd("fancy-editormd", {
                // ...
                imageUpload : true,
                imageFormats : [ "jpg", "jpeg", "gif", "png", "bmp", "webp" ],
                imageUploadURL : "{{ url_for('.upload') }}",
            });
        });
    </script>
    
    11⁄22⁄18_7.png
    • 同域上传

    如果要同域上传,可以这样写,在path字段改为相应的图片上传目录即可:

    @admin_bp.route('/upload/',methods=['POST'])
    @login_required
    def upload():
        file=request.files.get('editormd-image-file')
        if not file:
            res={
                'success':0,
                'message':'上传失败'
            }
        else:
            ex=os.path.splitext(file.filename)[1]
            filename=datetime.now().strftime('%Y%m%d%H%M%S')+ex
            file.save(filename)
            res={
                'success':1,
                'message':'上传成功',
                'url':url_for('.image',name=filename)
            }
        return jsonify(res)
    
    @admin_bp.route('/image/<name>')
    @csrf.exempt
    def image(name):
        with open(os.path.join('../uploads',name),'rb') as f:
            resp=Response(f.read(),mimetype="image/jpeg")
        return resp
    

    • 跨域上传

    如果跨域上传,国内图床可以选则常用的七牛云或者阿里云,都大同小异。
    这里我以七牛云为例, 七牛云有提供Python-SDK,还是很便利的,另外Github有Flask-QiniuStorage——七牛云存储Flask扩展,Qiniu Storage for Flask
    使用教程简单明了,首先pip安装:(利用pipenv)

    pipenv install Flask-QiniuStorage
    

    工厂函数中将其实例化:

    from flask_qiniustorage import Qiniu
    # ...
    qiniu_store = Qiniu()
    # ...
    

    from cryptic.extensions import qiniu_store
    # ...
    def register_extensions(app):
    qiniu_store.init_app(app)
    # ...
    

    类组织配置,Access key 和 Secret key比较敏感,我们选择从环境变量中读取,对应设置即可:

    QINIU_ACCESS_KEY = os.getenv('ACCESS_KEY')
    QINIU_SECRET_KEY = os.getenv('QINIU_KEY')
    QQINIU_BUCKET_NAME = '七牛空间名称'
    QINIU_BUCKET_DOMAIN = '七牛空间对应域名'
    

    这边七牛云的后台设置告一段落,我们回头看下Editor.MD
    可知文件接收的参数为editormd-image-file
    前端需要回调一个固定格式,用于告知前端状态信息与导入的URL地址,如果调用失败无需返回url。

    res.json({
        success : 1, 
        message : "这里随便",
        url: imageSrc
    })
    

    此时我们差不多就可以编辑上传路由了:
    设置文件接收的参数,方法为POSTonly

    admin.py:

    import os
    from datetime import datetime
    from flask import jsonify, request
    from flask_login import login_required
    from xxxxx.extensions import qiniu_store
    # ...
    @admin_bp.route('/upload/',methods=['POST'])
    @login_required
    def upload():
    data=request.files['editormd-image-file']
    if not data:
    res={
        'success':0,
        'message':'upload failed'
    }
    else:
    ex=os.path.splitext(data.filename)[1]
        filename=datetime.now().strftime('%Y%m%d%H%M%S')+ex
        qiniu_store.save(file, filename)
        res={
            'success':1,
            'message':'upload success',
            'url':qiniu_store.url(filename)
        }
    return jsonify(res)
    

    我们尝试使用request获取文件,遇到了新问题,我们收获了一个400错误

    如果前面定义了CSRF错误响应捕捉。此时我们就收到了一个CSRFError

    11⁄22⁄18_5.png

    由于Flask-WTF的CSRF保护开启,然而Editor.md 的上传表单中并没有包含csrftoken。
    要么就都添加csrf验证,要么就都关闭
    你可以阅览Flask-WTF的文档(http://www.pythondoc.com/flask-wtf/csrf.html)

    • 我们可以通过修改请求文件editormd/plugins/image-dialog/image-dialog.js来添加csrfToken来解决:

    image-dialog.js:

    if (settings.crossDomainUpload)
    {
        action += "&callback=" + settings.uploadCallbackURL + "&dialog_id=editormd-image-dialog-" + guid;
    }
    var csrfToken = $('meta[name="_token"]').attr('content');
    var csrfField = "";
    if (csrfToken) {
       csrfField = "<input type='hidden' name='_token' value='" + csrfToken + "' />";
    }
    

    修改响应字段,添加csrfField 变量,修改dialogContent为:

    var dialogContent = ( (settings.imageUpload) ? "<form action=\"" + action +"\" target=\"" + iframeName + "\" method=\"post\" enctype=\"multipart/form-data\" class=\"" + classPrefix + "form\">" : "<div class=\"" + classPrefix + "form\">" ) +  
                        ( (settings.imageUpload) ? "<iframe name=\"" + iframeName + "\" id=\"" + iframeName + "\" guid=\"" + guid + "\"></iframe>" : "" ) +  
                        "<label>" + imageLang.url + "</label>" +  
                        "<input type=\"text\" data-url />" + (function(){  
                            return (settings.imageUpload) ? "<div class=\"" + classPrefix + "file-input\">" +  
                                                                "<input type=\"file\" name=\"" + classPrefix + "image-file\" accept=\"image/*\" />" +  
                                                                csrfField +  
                                                                "<input type=\"submit\" value=\"" + imageLang.uploadButton + "\" />" +  
                                                            "</div>" : "";  
                        })() +  
                        "<br/>" +  
                        "<label>" + imageLang.alt + "</label>" +  
                        "<input type=\"text\" value=\"" + selection + "\" data-alt />" +  
                        "<br/>" +  
                        "<label>" + imageLang.link + "</label>" +  
                        "<input type=\"text\" value=\"http://\" data-link />" +  
                        "<br/>" + csrfField +  
                    ( (settings.imageUpload) ? "</form>" : "</div>");  
    

    这里排版有点乱,只有单独起行的的两个csrfField +,接下来在post表单做相应修改即可。

    • 不过既然我选择了惰性加载CsrfProtect,我暂时可以先直接通过添加@csrf_exempt在view里排除。
      于是最终上传代码如下:

    admin.py:

        
        import os, base64
        from datetime import datetime
        from flask import jsonify, request
        from flask_login import login_required
        from xxxxx.extensions import csrf, qiniu_store
        # ...
        @admin_bp.route('/upload/',methods=['POST'])
        @login_required
        @csrf_exempt
        def upload():
        data=request.files['editormd-image-file']
        if not data:
        res={
            'success':0,
            'message':'图片失败请重试'
        }
        else:
        ex=os.path.splitext(data.filename)[1]
            filename=datetime.now().strftime('%Y%m%d%H%M%S')+ex
            file = data.stream.read()
            file = base64.b64encode(file)
            # data.save(filename)
            qiniu_store.save(file, filename)
            res={
                'success':1,
                'message':'图片上传成功',
                'url':qiniu_store.url(filename)
            }
        return jsonify(res)
    

    jsonify自动添加文件头,成功回调的效果如下。

    11⁄22⁄18_4.png

    至此应该都没什么问题了,如果需要显示emoji或者代码高亮这些直接在js脚本里添加相应字段即可,添加必要的css和js文件。

    相关文章

      网友评论

        本文标题:基于Editor.MD的Flask图片实现

        本文链接:https://www.haomeiwen.com/subject/ltymqqtx.html