2013-09-30 40 views

回答

1

python-requests似乎支持自定義集合作爲要上載的文件的列表/映射。

這反過來應該允許我添加自定義標題字段,每個文件在一個分段上傳中上傳。

沒有。列表/映射中的每個文件必須是文件對象(或內容字符串),文件名和文件對象(或內容)的二元組,或文件名,文件對象(或內容)和文件的三元組類型。其他任何都是非法的。

除非#1640被上游接受,在這種情況下,您只需使用文件名,文件對象(或內容),文件類型和標題字典的4元組。


在這一點上正確的做法可能是使用不同的庫。例如,如果您直接使用urllib3而不是通過request包裝器,它會使您想要的操作變得相當容易。你只需要處理所有使用urllib3而不是requests的冗餘。

與此同時,您可以針對requests提交功能請求,並且未來可能會添加更簡單的方法。


但是,如果您需要的功能都是隱藏在屏幕下方,您無法實現,但有點令人沮喪。

看起來乾淨地做事會是一場噩夢。但是這些代碼都非常簡單,我們可以很輕鬆地進入它,所以讓我們這樣做。

看看requests.PreparedRequest.prepare_body。它期望files中的每個文件都是文件名,內容或文件對象的元組,以及可選的內容類型。它基本上只讀取任何文件對象以將其轉換爲內容,並將所有內容直接傳遞給urllib3.filepost.encode_multipart_formdata。所以,除非我們想要替換這個代碼,否則我們需要用這些值中的一個將頭文件走私。讓我們通過(filename, contents, (content_type, headers_dict))來做到這一點。所以,requests本身沒有改變。

那它所稱的urllib3.filepost.encode_multipart_formdata呢?正如你所看到的,如果你爲文件傳遞元組,它會調用一個名爲iter_field_objects的函數,最終每個函數都會調用urllib3.fields.RequestField.from_tuples。但是如果你看看from_tuples替代構造函數,它表示它在那裏處理構造RequestField對象的「舊式」方式,並且正常的構造函數用於「新式」方式,實際上它可以讓你通過頭。

所以,我們所需要做的就是monkeypatch iter_field_objects,用一種使用新風格的方式取代最後一行,我們應該完成。讓我們嘗試:

import requests 
import requests.packages.urllib3 
from requests.packages.urllib3.fields import RequestField, guess_content_type 
import six 

old_iter_field_objects = requests.packages.urllib3.filepost.iter_field_objects 
def iter_field_objects(fields): 
    if isinstance(fields, dict): 
     i = six.iteritems(fields) 
    else: 
     i = iter(fields) 

    for field in i: 
     if isinstance(field, RequestField): 
     yield field 
     else: 
     name, value = field 
     filename = value[0] 
     data = value[1] 
     content_type = value[2] if len(value)>2 else guess_content_type(filename) 
     headers = None 
     if isinstance(content_type, (tuple, list)): 
      content_type, headers = content_type 
     rf = RequestField(name, data, filename, headers) 
     rf.make_multipart(content_type=content_type) 
     yield rf 
requests.packages.urllib3.filepost.iter_field_objects = iter_field_objects 

現在:

>>> files = {'file': ('foo.txt', 'foo\ncontents\n'), 
...   'file2': ('bar.txt', 'bar contents', 'text/plain'), 
...   'file3': ('baz.txt', 'baz contents', ('text/plain', {'header': 'value'}))} 
>>. r = request.Request('POST', 'http://example.com', files=files) 
>>> print r.prepare().body 
--1ee28922d26146e7a2ee201e5bf22c44 
Content-Disposition: form-data; name="file3"; filename="baz.txt" 
Content-Type: text/plain 
header: value 

baz contents 
--1ee28922d26146e7a2ee201e5bf22c44 
Content-Disposition: form-data; name="file2"; filename="bar.txt" 
Content-Type: text/plain 

bar contents 
--1ee28922d26146e7a2ee201e5bf22c44 
Content-Disposition: form-data; name="file"; filename="foo.txt" 
Content-Type: text/plain 

foo 

田田!

請注意,您需要使用相對最新的requests/urllib3才能正常工作。我認爲requests 2.0.0就足夠了。

+0

太棒了!我現在可以堅持這一點,並向上遊請求實現一個元組擴展,希望下一個版本支持這個:) –

+0

@qarma:如果你要[添加功能請求](https://github.com/kennethreitz/requests/issues/new),請儘快填寫並在此鏈接。因爲我可能自己編寫一個補丁程序,而且我不想創建一個與您的功能請求重複但沒有連接的請求。 – abarnert

+0

https://github.com/kennethreitz/requests/issues/1639 –

相關問題