最近有個項目,叫作文檔服務資源中心,相似於七牛,爲各個業務系統提供統一的文件資源服務,包括文件的存儲、操做管理、下載、預覽等。在作文件存儲的時候,遇到了這個當指定上傳的文件名爲中文時,Flask框架的服務端沒法解析成文件,而是當成通常的表單數據處理。咱們在文件存儲的實現架構以下圖:python
客戶端業務系統(Python開發的)經過調用python-sdk中的上文文件API上傳文件。按照requests這個類庫上傳文件的格式要求,必須指明文件的文件名。因此,在API開發完成以後,當上傳的文件的文件名是中文的時候,測試沒經過。flask
客戶端測試代碼:架構
請注意,在files變量中,file對應的元組值的第一個參數「十三五」發展規劃.docx」就是文件名,是中文格式。框架
服務端代碼(簡化後):工具
注意,在try..exception中的代碼,判斷是否獲取文件成功。post
運行:測試
先運行服務端代碼,而後運行測試代碼。結果以下:網站
進入調試模式,查看request變量的值,重點關注files跟form屬性。以下圖:編碼
從上圖能夠,files的屬性爲空,而把file當成了form數據的屬性,屬性的值爲文件的二進制內容數據。url
(1.)下載fiddler抓包工具。發現requests向flask網站服務傳遞以下數據。
特別注意,紅框中的filename*這一段。
(2.)讀Flask的源代碼,特別注意Flask對上傳文件的解析與處理。發現位於werkzeug下的formparser.py裏的parse_lines方法中判斷語句(位於文件的413行)
能夠得出結論,Flask是根據名稱爲filename的鍵來判斷Requests傳遞過來的數據是不是文件內容。而在上面經過fiddler抓包工具可知,Requests傳遞了filename*這個鍵的名稱,多了一個*號。因此,Flask認爲不是傳遞的文件,從而當成了通常屬性處理。
(3.)那Requests爲何會傳遞filename*這樣的鍵呢。再次跟蹤並閱讀Requests的源代碼。返現Requests會對filename作編碼的特殊處理。代碼位於requestsàpackagesàurllib3—>fields.py(第22行的format_header_param方法)。
對不是ascii編碼的內容,進行了rfc 2231編碼,並組織成key*= rfc 2231這種格式。因此就有了上述的filename*這種格式的鍵值對。
有三種解決辦法。
(1.)修改Requests的源代碼
requestsàpackagesàurllib3—>fields.py—>format_header_param方法的如下代碼
改爲
(2.)修改Flask的源代碼
werkzeug下的formparser.py裏的parse_lines方法中的413行開始的如下代碼
改爲
並導入相應的包。
(3.)修改調用Request的post方法時files變量的filename賦值,將其改爲英文,好比設置成固定的file_name,而將真正的filename(最好帶有後綴)當成data參數中的鍵名爲file_name(根據項目狀況,自由定義)的值傳遞給服務端,服務端去讀取file_name對應的值就行。實現代碼以下:
總結:修改Flask、requests類庫的源代碼不太理想,不便於部署,並且可能會發生其餘意想不到的問題。建議採用第3中折中解決辦法。