近期專案開發中,隔壁那個搞python的哥們竟然笑著對我說,希望我能給他寫1個百度編輯器的demo,方便他直接呼叫。
當時真的受寵若驚,這哥們實力不在我之下,只能答應它了。上網搜尋下,有1篇文章Flask專案整合富文字編輯器UEditor實現圖片上傳功能已經有1個現成的例子了。
這篇文章的作者,直接在檢視中定義對應的操作,進行一系列圖片上傳功能的。但是,這並沒有滿足我的要求,我想要的想過是直接匯入一個模組,然後它幫我做完一切的事情。比如這樣的形式:
1 2 3 4 |
from xxx import yyy app = Flask(__name__) yyy(app) |
於是只好從頭開始學習。
這裡,我按照如下的方式從頭編寫1個Flask版本的百度編輯器的外掛:
- 百度文件的解讀
- 後端實際程式碼的編寫
- 前端程式碼的編寫
下面我們開始我們編寫外掛的過程。
百度文件的解讀
在百度文件中,已經說明UEditor提供4種後臺語言,分別為php,asp,asp.net和jsp。首先需要配置serverUrl引數,當ueditor初始化會向serverUrl中的URL發起對後端配置的請求。
而配置的優先順序如下:
1 2 |
後端獲取的配置項 > 例項化傳入的配置項 > ueditor.config.js檔案的配置項 |
可以看到,後端獲取的配置項優先順序是最高的。在對後端配置請求的過程中,會對配置檔案config.json
中的設定進行讀取。
而在請求的過程中,百度文件統一請求格式說明中已經說明,通過GET請求上的action引數來判斷是什麼型別的請求。後端根據不同的請求,進行對應的處理後,需要返回給定格式的內容給前端Javascript。返回的結果一般為JSON的形式。
實現目標
現在我們已經對百度編輯器UEditor的文件進行了初步的解讀,下面我們開始編寫我們實際的程式碼。
在這裡,我們主要會用到config和uploadimage這2個配置,其他功能留給讀者自己去實現。
實際程式碼的編寫
在編寫程式碼之前,我打算這樣來實現這個外掛:
- 配置檔案為1個python的模組
- 有1個模組用於處理對應請求的操作
- 有1個模組用於處理圖片上傳的操作,這裡直接拿取之前Flask文件作者中的上傳模組
然後我們逐一進行講解。
配置檔案
這裡,我打算將百度預設提供的配置寫入到1個config的模組中,本來打算使用類似如下的方式:
1 2 3 |
imageAction = "uploadimage" imageFieldName = "upfile" ... |
由於時間比較緊,一時半會做不到Flask中讀取配置檔案後為1個字典的形式,因此簡化為該模組直接返回1個字典,如下所示:
1 2 3 4 5 6 7 8 9 10 |
CONFIG = dict(imageActionName = "uploadimage", # 執行上傳圖片的action名稱 imageFieldName = "upfile", #提交的圖片表單名稱 imageMaxSize = 2048000, #上傳大小限制,單位B imageAllowFiles = ['.png', '.jpg', '.jpeg', '.gif', '.bmp'], #上傳圖片格式顯示 imageCompressEnable = True, #是否壓縮圖片,預設是true imageCompressBorder = 1600, #圖片壓縮最長邊限制 imageInsertAlign = "none", #插入的圖片浮動方式 imageUrlPrefix = "", #圖片訪問路徑字首 imagePathFormat = "upload/{yyyy}/{mm}/{dd}/{time}{rand:6}" ) |
我們將這段程式碼定義為config模組中,這樣,我們就完成了配置模組的內容了。
處理請求
這裡我們定義1個ueditor的模組用於我們處理請求的操作。這裡,我定義了1個UEditor的類用於處理這個操作:
1 2 |
class UEditor(object): pass |
首先我們要做的第一步是解決請求引數的問題,讓不同的請求呼叫不同的處理函式,我們將其定義在該類的get_action
函式中:
1 2 3 4 |
def get_action(self): action = request.args.get('action') if action: return self.handle(action) |
我們將具體處理的過程放在handle函式中,在這個函式中,我們要做2件事情:
- 根據不同的請求引數呼叫不同的函式
- 將不同函式返回的結果返回給呼叫者
然後是handle函式其具體的原始碼:
1 2 3 4 5 6 7 8 9 10 11 |
def handle(self, action): if action == 'config': result = get_config() elif action == 'uploadimage': result = upload_image() else: result = {'state': '未實現'} res = make_response(json.dumps(result)) res.headers['Access-Control-Allow-Origin'] = '*' res.headers['Access-Control-Allow-Headers'] = 'X-Requested-With,X_Requested_With' return res |
可以看到,這部分的原始碼和我們之前編寫的並沒有什麼不同。在action時我們應該將配置檔案中的內容直接返回即可:
1 2 |
def get_config(): return CONFIG |
而在上傳圖片的時候,我們根據配置檔案中的配置,將其傳遞給上傳檔案的模組,這裡我們直接把之前Flask那篇文章作者git上的Uploader拿了過來。其實他也是參考php的實現的原始碼,將其修改為python版本而言。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def upload_image(): """上傳圖片""" fieldName = CONFIG.get('imageFieldName') conf = dict(pathFormat = CONFIG.get('imagePathFormat'), maxSize = CONFIG.get('imageMaxSize'), allowFiles = CONFIG.get('imageAllowFiles') ) if request.files.get(fieldName): field = request.files[fieldName] uploader = Uploader(field, conf, 'static') result = uploader.getFileInfo() else: result = {'state': '上傳介面出錯'} return result |
這裡,在Uploader類中第1個引數為類檔案物件,第2個引數為對應的配置,第3個引數為圖片上傳的根目錄。最後,通過這個例項的getFileInfo方法得到後端上傳成功後返回的內容。
這樣,我們就基本解決了後端圖片上傳的過程了。但是,前端的呼叫問題我們完全還沒有涉及到,下面我們來說說前端的呼叫問題。
前端呼叫
關於前端呼叫的問題,我個人的設想是在1個模板檔案中我們引入百度UEditor對應的Javascript檔案,然後我們初始化對應的物件後,設定serverUrl為我們給定的URL地址即可操作了。一般我們的程式碼是這樣的:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<script type="text/javascript" charset="utf-8" src="static/ueditor.config.js"></script> <script type="text/javascript" charset="utf-8" src="static/ueditor.all.min.js"> </script> <script type="text/javascript" charset="utf-8" src="static/lang/zh-cn/zh-cn.js"></script> <div> <div id="editor" style="height:400px;"></div> </div> <script type="text/javascript"> var ue = UE.getEditor('editor', { serverUrl: "xxxx/yyyyy" }); </script> |
之前我們後端程式碼的過程只解決了圖片上傳的問題,關於serverUrl關聯到對應URL的問題還一直沒有解決。
為了解決這個問題,我打算引入藍圖和Flask提供的add_url_rule
來解決這個問題。
1 2 3 4 5 6 7 8 9 10 |
UE = Blueprint('ueditor', __name__, url_prefix= '/ueditor') ... def init_app(self,app): self.app = app UE.add_url_rule('/upload/' 'uploads', self.get_action, methods = ['POST', 'GET', 'OPTIONS']) self.app.register_blueprint(UE) |
然後我們讓Flask應用例項註冊這個藍圖,這樣當我們伺服器啟動時,就可以使用這個藍圖url地址了。
總結
最後,我們總結下這個外掛的使用方法,我們新建1個app模組,其原始碼類似如下:
1 2 3 4 5 6 7 8 9 |
from flask import Flask, render_template from ueditor import UEditor app = Flask(__name__) ue = UEditor(app) @app.route('/') def index(): return render_template('index.html') |
在這裡,我們引入我們之前編寫的外掛模組,然後將其例項化操作。而在index.html檔案中則為我們之前引入百度編輯器的內容。
在這裡index.html
的內容類似如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<script type="text/javascript" charset="utf-8" src="/static/ueditor/ueditor.config.js"></script> <script type="text/javascript" charset="utf-8" src="/static/ueditor/ueditor.all.min.js"> </script> <script type="text/javascript" charset="utf-8" src="/static/ueditor/lang/zh-cn/zh-cn.js"></script> <div> <div id="editor" style="height:400px;"></div> <button id="submit">提交</button> </div> <script type="text/javascript"> var ue = UE.getEditor('editor', { serverUrl: "/ueditor/upload/" }); </script> |
這樣我們便完成了百度編輯器的1個外掛的編寫了。
參考文章:
http://fex-team.github.io/ueditor/#server-deploy
http://fex-team.github.io/ueditor/#server-config
http://fex-team.github.io/ueditor/#dev-request_specification