前端小誌(轉型中)

一個用來記錄人老會忘記的地方

Python request post(上傳) utf-8問題

2019年03月10日
不知道多久沒有玩前端了,教練我想研究react hook啊... 現在我的前端實在是無用舞之地,來記錄一些工作上遇到的事情吧。 這邊要描述的是python requests(post) multipart/form-data的問題。

公司需要大量轉移檔案,於是我們寫了一隻小程式去爬清單,然後將資料從一個網頁下載上傳至另一個網頁。

一開始測試都很ok,直到我們遇到了一個使用日文名稱的檔案。

在交叉測試之後,下載那邊是沒有問題的,我們使用linux,雖然在command line看到的檔名是亂碼,但是不影響實際上使用。

上傳則是一直 http code 500,訊息也只有internal server error。

import requests
requests.post('url', headers=headers, files={'file': ('日文.zip', ByteIO(content))})

我的直覺就是檔名的問題(公司有很多日文檔案,每次都被婊),於是上網google了一下。

看到了這篇

然後點進去了他的blog看了一下

# coding: utf8
import requests
import re

def rewrite_request(prepared_request):
    filename = u'tête-à-tête.txt'.encode('utf-8')
    prepared_request.body = re.sub(b'filename\*=.*', b'filename=' + filename, prepared_request.body)
    return prepared_request

requests.post('http://localhost/upload.php', files={
    'fieldname': (u'tête-à-tête.txt', 'some contents')
}, auth=rewrite_request)

我實際跑了一下,發現什麼都沒做的request.post,body的filename吃到utf-8檔名確實會自動消失,上傳沒有檔名的檔案當然會爆炸。

不過blog的解法是一個醜醜的workaround,於是我研究了一下preparerequest自己寫了一個

import requests
s = requests.Session()
prepped = s.prepare_request(requests.Request('POST', url,
    headers=headers, files={'file': ('IWLLREPLACETHIS',io.BytesIO(attachment_file)) }))

prepped.body = re.sub(b'IWLLREPLACETHIS', attachment_name.encode('utf-8'), prepped.body)

prepped.headers['content-length'] = len(prepped.body)

settings = s.merge_environment_settings(prepped.url, {}, None, None, None)
r = s.send(prepped, **{**settings, 'timeout': 10 })

我先塞了一個簡易但不容易重複的字串在filename裡,然後直接取代字串。

另外需要更新headers的content-length,replace body裡面的filename會改變整個body的size,像在我們的api上傳檔案會檢查content-length與body size是否一致,若是不修正一樣是跳internal server error。

這個小問題卡了我1-2個小時檢查,2-3個小時測試+寫code,最後又卡1小時在content-length

寫到這裡,一切都是requests裡的Urllib3的錯!!!!(python2的版本好像是好的)



來追根究底一下

嗯....看了很久 看不太懂,我說說我的想法

Urllib3的filename是吃rfc2231 spec的,長出來應該是這樣filename*=UTF-8''name(但我記得我自己實測name是空的)

但是html 5在multipart/form-data有自己的spec,不支援2231 spec,所以不管怎樣都沒用

在github上已經討論了很久,也有人開了pr,不過urllib3不是只寫給html的,算是一個big change,所以一直沒有merge

網路上如果google python 上傳 中文,應該是一堆文章,但不要跟那些文章一樣去改requests的source code啊

太暴力了XDDDDD


展開Disqus
分類
最近文章
友站連結
© 2019 Ernie Yang