Flask下如何處理Requests 上傳中文文件名的問題


一、問題的由來

    最近有個項目,叫做文檔服務資源中心,類似於七牛,為各個業務系統提供統一的文件資源服務,包括文件的存儲、操作管理、下載、預覽等。在做文件存儲的時候,遇到了這個當指定上傳的文件名為中文時,Flask框架的服務端無法解析成文件,而是當成一般的表單數據處理。我們在文件存儲的實現架構如下圖:

clip_image002

        客戶端業務系統(Python開發的)通過調用python-sdk中的上文文件API上傳文件。按照requests這個類庫上傳文件的格式要求,必須指明文件的文件名。所以,在API開發完成之后,當上傳的文件的文件名是中文的時候,測試沒通過。

 

二、代碼解析

        客戶端測試代碼:

image

 

     請注意,在files變量中,file對應的元組值的第一個參數“十三五”發展規划.docx”就是文件名,是中文格式。

        服務端代碼(簡化后):

image

           注意,在try..exception中的代碼,判斷是否獲取文件成功。

     運行:

         先運行服務端代碼,然后運行測試代碼。結果如下:

clip_image002

          進入調試模式,查看request變量的值,重點關注files跟form屬性。如下圖:

clip_image004

      從上圖可以,files的屬性為空,而把file當成了form數據的屬性,屬性的值為文件的二進制內容數據。

三、問題緣由的查找

 

(1.)下載fiddler抓包工具。發現requests向flask網站服務傳遞如下數據。

clip_image002[5]

特別注意,紅框中的filename*這一段。

(2.)讀Flask的源代碼,特別注意Flask對上傳文件的解析與處理。發現位於werkzeug下的formparser.py里的parse_lines方法中判斷語句(位於文件的413行)

image

        可以得出結論,Flask是根據名稱為filename的鍵來判斷Requests傳遞過來的數據是否是文件內容。而在上面通過fiddler抓包工具可知,Requests傳遞了filename*這個鍵的名稱,多了一個*號。所以,Flask認為不是傳遞的文件,從而當成了一般屬性處理。

(3.)那Requests為什么會傳遞filename*這樣的鍵呢。再次跟蹤並閱讀Requests的源代碼。返現Requests會對filename做編碼的特殊處理。代碼位於requestsàpackagesàurllib3—>fields.py(第22行的format_header_param方法)。

clip_image002[7]

     對不是ascii編碼的內容,進行了rfc 2231編碼,並組織成key*= rfc 2231這種格式。所以就有了上述的filename*這種格式的鍵值對。

四、解決辦法

有三種解決辦法。

(1.)修改Requests的源代碼

requestsàpackagesàurllib3—>fields.py—>format_header_param方法的以下代碼

image

改成

image

(2.)修改Flask的源代碼

werkzeug下的formparser.py里的parse_lines方法中的413行開始的以下代碼

image

改成

image

並導入相應的包。

 

(3.)修改調用Request的post方法時files變量的filename賦值,將其改成英文,比如設置成固定的file_name,而將真正的filename(最好帶有后綴)當成data參數中的鍵名為file_name(根據項目情況,自由定義)的值傳遞給服務端,服務端去讀取file_name對應的值就行。實現代碼如下:

clip_image002[9]

      總結:修改Flask、requests類庫的源代碼不太理想,不便於部署,而且可能會發生其他意想不到的問題。建議采用第3中折中解決辦法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM