6

我想将文件上传到网络服务器。根据我的阅读,最好的方法是在 HTTP POST 请求上使用 multipart/form-data 编码类型。

我的研究似乎表明使用 Python 标准库没有简单的方法可以做到这一点。我正在使用 Python 3。

(注意:请参阅名为requests ( PyPI 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())

这适用于发送文本。

有两个问题:这只是文本,整个文本文件必须作为一个巨大的字符串存储在内存中。

如何上传任何二进制文件?有没有办法在不将整个文件读入内存的情况下做到这一点?

4

3 回答 3

3

看看小Doug Hellmann 的 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
于 2013-04-03T10:36:52.020 回答
2

我看过这个模块

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'
于 2013-04-03T07:22:22.193 回答
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

于 2016-01-19T16:55:10.627 回答