2013-04-02 82 views
4

我想將文件上傳到Web服務器。從我讀過的,最好的方法是在HTTP POST請求上使用multipart/form-data編碼類型。Python - HTTP multipart/form-data POST請求

我的研究似乎表明,沒有簡單的方法來使用Python標準庫來完成此操作。我使用Python 3

(注:看到一個名爲requestsPyPI Link)來輕鬆地完成這個包)

我目前使用這種方法:

import mimetypes, http.client 
boundary = 'wL36Yn8afVp8Ag7AmP8qZ0SA4n1v9T' # Randomly generated 
for fileName in fileList: 
    # Add boundary and header 
    dataList.append('--' + boundary) 
    dataList.append('Content-Disposition: form-data; name={0}; filename={0}'.format(fileName)) 

    fileType = mimetypes.guess_type(fileName)[0] or 'application/octet-stream' 
    dataList.append('Content-Type: {}'.format(fileType)) 
    dataList.append('') 

    with open(fileName) as f: 
     # Bad for large files 
     dataList.append(f.read()) 

dataList.append('--'+boundary+'--') 
dataList.append('') 
contentType = 'multipart/form-data; boundary={}'.format(boundary) 

body = '\r\n'.join(dataList) 
headers = {'Content-type': contentType} 

conn = http.client.HTTPConnection('http://...') 
req = conn.request('POST', '/test/', body, headers) 

print(conn.getresponse().read()) 

本工程以發送文本。

有兩個問題:這只是文本,整個文本文件必須作爲一個巨大的字符串存儲在內存中。

如何上傳任何二進制文件?有沒有辦法做到這一點,而無需將整個文件讀入內存?

+1

你應該使用你已經提到的請求包。有很多東西不是由標準庫處理的,比如會話管理,認證,檢查SSL證書......你不使用請求模塊的原因是什麼? – Achim

+0

您可以將代碼從請求模塊中複製出來,然後執行您所需的操作。 – User

+1

這當然是你的決定。但是如果我可以選擇打包高質量,經過充分測試的「使命證明」,......圖書館或我自己的自定義代碼,我總是會選擇第一個。 ;-) – Achim

回答

1

我看了一下這個模塊

class HTTPConnection: 
    # ... 
    def send(self, data): # line 820 
     """Send `data' to the server. 
     ``data`` can be a string object, a bytes object, an array object, a 
     file-like object that supports a .read() method, or an iterable object. 
     """ 

數據是完全體。 您可以傳遞這樣一個迭代器:(我沒有嘗試一下)

def body(): 
    for fileName in fileList: 
    # Add boundary and header 
    yield('--' + boundary) + '\r\n' 
    yield('Content-Disposition: form-data; name={0}; filename= {0}'.format(fileName)) + '\r\n' 

    fileType = mimetypes.guess_type(fileName)[0] or 'application/octet-stream' 
    yield('Content-Type: {}'.format(fileType)) + '\r\n' 
    yield('\r\n') 

    with open(fileName) as f: 
     # Bad for large files 
     yield f.read() 
    yield('--'+boundary+'--') + '\r\n' 
    yield('') + '\r\n' 
+0

這是一個好主意,並在3.2或3.3中進行了一些修改。如果給HTTPConnection.request或HTTPConnection.send一個迭代器,那麼也必須指定內容長度頭。 但是,我正在使用Python 3.1。也許這是不可行的。 – William

+0

你用python 3.1得到了什麼問題? – User

+0

在3.1'body'不能是一個可迭代的。我們實際上已經從3.1移開,所以這回答了我的問題。 – William

2

看看小Doug Hellmann's urllib2,由我翻譯成python3。

我用它幾乎是這樣的:

import urllib.request 
import urllib.parse 
from lib.multipart_sender import MultiPartForm 

myfile = open('path/to/file', 'rb') 
form = MultiPartForm() 
form.add_field('token', apipost[mycgi['domain']]._token) 
form.add_field('domain', mycgi['domain']) 
form.add_file('file', 'logo.jpg', fileHandle=myfile) 
form.make_result() 

url = 'http://myurl' 
req1 = urllib.request.Request(url) 
req1.add_header('Content-type', form.get_content_type()) 
req1.add_header('Content-length', len(form.form_data)) 
req1.add_data(form.form_data) 
fp = urllib.request.urlopen(req1) 
print(fp.read()) # to view status 
+0

謝謝,但想法是使用標準庫。 – William

0

您可以使用unirest來撥打電話。示例代碼

import unirest 

# consume async post request 
def consumePOSTRequestSync(): 
params = {'test1':'param1','test2':'param2'} 

# we need to pass a dummy variable which is open method 
# actually unirest does not provide variable to shift between 
# application-x-www-form-urlencoded and 
# multipart/form-data 
params['dummy'] = open('dummy.txt', 'r') 
url = 'http://httpbin.org/post' 
headers = {"Accept": "application/json"} 
# call get service with headers and params 
response = unirest.post(url, headers = headers,params = params) 
print "code:"+ str(response.code) 
print "******************" 
print "headers:"+ str(response.headers) 
print "******************" 
print "body:"+ str(response.body) 
print "******************" 
print "raw_body:"+ str(response.raw_body) 

# post sync request multipart/form-data 
consumePOSTRequestSync() 

退房的博客文章瞭解更多詳情http://stackandqueue.com/?p=57