基于Editor.MD的Flask图片实现

Editor.MD的Flask图片上传实战

继上篇 基于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文件。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容