2012-11-11 66 views
5

我試圖用Python &替換curl的請求庫。使用curl,我可以使用curl -T選項將單個XML文件上載到REST服務器。我一直無法對請求庫做同樣的事情。用Python請求庫上傳一個大的XML文件

基本方案的工作原理:

payload = '<person test="10"><first>Carl</first><last>Sagan</last></person>' 
headers = {'content-type': 'application/xml'} 
r = requests.put(url, data=payload, headers=headers, auth=HTTPDigestAuth("*", "*")) 

當我打開一個XML文件改變有效載荷更大的字符串,該方法。把掛起(我用的編解碼器庫,以獲得正確的unicode字符串)。例如,具有66KB文件:

xmlfile = codecs.open('trb-1996-219.xml', 'r', 'utf-8') 
headers = {'content-type': 'application/xml'} 
content = xmlfile.read() 
r = requests.put(url, data=content, headers=headers, auth=HTTPDigestAuth("*", "*")) 

我一直在尋找到使用多選項(文件),但服務器似乎並不喜歡這樣。

所以我想知道是否有一種方法來模擬Python請求庫中的curl -T行爲。

UPDATE 1: 程序掛在textmate中,但在命令行中引發UnicodeEncodeError錯誤。似乎一定是問題。所以問題是:有沒有辦法將unicode字符串發送到服務器與請求庫?

更新2: 感謝Martijn Pieters的評論,UnicodeEncodeError消失了,但出現了一個新問題。 隨着一個文本(ASCII)的XML字符串,日誌顯示下列行:

2012-11-11 15:55:05,154 INFO Starting new HTTP connection (1): my.ip.address 
2012-11-11 15:55:05,294 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 401 211 
2012-11-11 15:55:05,430 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 201 0 

似乎服務器總是彈跳第一認證嘗試,但然後接受第二個(?)。

隨着文件對象(打開( 'TRB-1996-219.xml', 'RB'))傳遞到數據,日誌文件顯示:

2012-11-11 15:50:54,309 INFO Starting new HTTP connection (1): my.ip.address 
2012-11-11 15:50:55,105 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 401 211 
2012-11-11 15:51:25,603 WARNING Retrying (0 attempts remain) after connection broken by 'BadStatusLine("''",)': /v1/documents?uri=/example/test.xml 

因此,首先嚐試如前阻擋,但沒有第二次嘗試。

根據馬亭皮特斯(下),第二個問題可以通過一個故障服務器(空行)說明。 我會研究這一點,但如果有人有一個解決方法(除了使用捲曲),我不會介意聽到它。

而我仍然感到驚訝的是,對於小字符串和文件對象,請求庫的行爲如此不同。無論如何,文件對象在到達服務器之前是否已序列化?

回答

9

要放大文件,不要將它們讀入內存。只需將文件傳遞作爲data關鍵字:

xmlfile = open('trb-1996-219.xml', 'rb') 
headers = {'content-type': 'application/xml'} 
r = requests.put(url, data=xmlfile, headers=headers, auth=HTTPDigestAuth("*", "*")) 

此外,你被打開文件以Unicode(UTF-8之間對其進行解碼)。由於您將它發送到遠程服務器,因此您需要原始字節而不是unicode值,並且應該將該文件作爲二進制文件打開。

+0

感謝您的快速反應。這樣做解決了UnicodeEncodeError,但引入了ConnectionError:MaxRetryError。而服務器沒有關閉,因爲我可以用curl上傳文件。 –

+0

@M_breeb:你需要使用'logging'模塊來找出原因;由於某種原因,連接到服務器的嘗試失敗,* *之前*能夠PUT數據。 'urllib3'連接池重試連接,並且它將記錄每次連接到'logging'模塊的失敗。 –

+0

'import logging',然後'logging.basicConfig()'是快速獲得輸出的最基本方法。 –

1

摘要式身份驗證始終要求您至少向服務器發出兩個請求。第一個請求不包含任何驗證數據。這個第一個請求將失敗,需要一個401「需要授權」響應代碼和一個摘要挑戰(稱爲聲明)來用於散列您的密碼等(具體細節無關緊要)。這用於向服務器發出第二個請求,其中包含您的憑據與挑戰進行散列處理。

的問題是在這兩步驗證:您的大文件已經與第一未經授權的請求發送(發送白白),但在第二次請求的文件對象是已經在EOF位置。由於文件大小也是在第二個請求的Content-length頭中發送的,這會導致服務器等待永遠不會發送的文件。

你可以解決它使用一個會話的請求,並先進行身份驗證一個簡單的要求(比如GET請求)。然後使用第一個請求的相同摘要質詢,再次發出包含實際負載的PUT請求。

sess = requests.Session() 
sess.auth = HTTPDigestAuth("*", "*") 
sess.get(url) 
headers = {'content-type': 'application/xml'} 
with codecs.open('trb-1996-219.xml', 'r', 'utf-8') as xmlfile: 
    sess.put(url, data=xmlfile, headers=headers) 
相關問題