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>
-
同域上传
如果要同域上传,可以这样写,在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
})
此时我们差不多就可以编辑上传路由了:
设置文件接收的参数,方法为POST
only
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
由于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自动添加文件头,成功回调的效果如下。
至此应该都没什么问题了,如果需要显示emoji或者代码高亮这些直接在js脚本里添加相应字段即可,添加必要的css和js文件。