基于Editor.MD的Flask圖片實現(xiàn)

Editor.MD的Flask圖片上傳實戰(zhàn)

繼上篇 基于Flask的Markdown編輯器實踐選擇的Editor.MD為博客提供的MarkDown編輯器自帶圖片上傳接口走孽,如果不使用Flask-Uploads的話也是很簡便的。這篇相當(dāng)于上篇的一個補充和拓展棵譬。

在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字段改為相應(yīng)的圖片上傳目錄即可:

@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

  • 跨域上傳

如果跨域上傳,國內(nèi)圖床可以選則常用的七牛云或者阿里云,都大同小異缕贡。
這里我以七牛云為例, 七牛云有提供Python-SDK拣播,還是很便利的晾咪,另外Github有Flask-QiniuStorage——七牛云存儲Flask擴展,Qiniu Storage for Flask
使用教程簡單明了贮配,首先pip安裝:(利用pipenv)

pipenv install Flask-QiniuStorage

工廠函數(shù)中將其實例化:

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比較敏感,我們選擇從環(huán)境變量中讀取泪勒,對應(yīng)設(shè)置即可:

QINIU_ACCESS_KEY = os.getenv('ACCESS_KEY')
QINIU_SECRET_KEY = os.getenv('QINIU_KEY')
QQINIU_BUCKET_NAME = '七胖缰空間名稱'
QINIU_BUCKET_DOMAIN = '七牛空間對應(yīng)域名'

這邊七牛云的后臺設(shè)置告一段落圆存,我們回頭看下Editor.MD
可知文件接收的參數(shù)為editormd-image-file
前端需要回調(diào)一個固定格式叼旋,用于告知前端狀態(tài)信息與導(dǎo)入的URL地址,如果調(diào)用失敗無需返回url沦辙。

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

此時我們差不多就可以編輯上傳路由了:
設(shè)置文件接收的參數(shù)夫植,方法為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錯誤響應(yīng)捕捉详民。此時我們就收到了一個CSRFError

11?22?18_5.png

由于Flask-WTF的CSRF保護開啟,然而Editor.md 的上傳表單中并沒有包含csrftoken陌兑。
要么就都添加csrf驗證沈跨,要么就都關(guān)閉
你可以閱覽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 + "' />";
}

修改響應(yīng)字段,添加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表單做相應(yīng)修改即可软驰。

  • 不過既然我選擇了惰性加載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自動添加文件頭碌宴,成功回調(diào)的效果如下杀狡。

11?22?18_4.png

至此應(yīng)該都沒什么問題了,如果需要顯示emoji或者代碼高亮這些直接在js腳本里添加相應(yīng)字段即可贰镣,添加必要的css和js文件呜象。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碑隆,隨后出現(xiàn)的幾起案子恭陡,更是在濱河造成了極大的恐慌,老刑警劉巖上煤,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件休玩,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機拴疤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門永部,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呐矾,你說我怎么就攤上這事苔埋。” “怎么了蜒犯?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵组橄,是天一觀的道長。 經(jīng)常有香客問我罚随,道長玉工,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任淘菩,我火速辦了婚禮遵班,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瞄勾。我一直安慰自己费奸,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布进陡。 她就那樣靜靜地躺著愿阐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪趾疚。 梳的紋絲不亂的頭發(fā)上缨历,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天,我揣著相機與錄音糙麦,去河邊找鬼辛孵。 笑死,一個胖子當(dāng)著我的面吹牛赡磅,可吹牛的內(nèi)容都是我干的魄缚。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼焚廊,長吁一口氣:“原來是場噩夢啊……” “哼冶匹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咆瘟,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤嚼隘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后袒餐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體飞蛹,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡谤狡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了卧檐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片墓懂。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖泄隔,靈堂內(nèi)的尸體忽然破棺而出拒贱,到底是詐尸還是另有隱情宛徊,我是刑警寧澤佛嬉,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站闸天,受9級特大地震影響暖呕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜苞氮,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一湾揽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧笼吟,春花似錦库物、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撵枢,卻和暖如春民晒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锄禽。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工潜必, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沃但。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓磁滚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宵晚。 傳聞我的和親對象是個殘疾皇子垂攘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容