<p>基本上,如果指定一个<code>files</code>参数(字典),那么<code>requests</code>将发送一个<code>multipart/form-data</code>帖子,而不是<code>application/x-www-form-urlencoded</code>帖子。但是,您不限于使用该词典中的实际文件:</p>
<pre><code>>>> import requests
>>> response = requests.post('http://httpbin.org/post', files=dict(foo='bar'))
>>> response.status_code
200
</code></pre>
<p>而httpbin.org让你知道你发布了什么标题;在<code>response.json()</code>中我们有:</p>
<pre><code>>>> from pprint import pprint
>>> pprint(response.json()['headers'])
{'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'close',
'Content-Length': '141',
'Content-Type': 'multipart/form-data; '
'boundary=c7cbfdd911b4e720f1dd8f479c50bc7f',
'Host': 'httpbin.org',
'User-Agent': 'python-requests/2.21.0'}
</code></pre>
<p>更好的是,您可以使用元组而不是单个字符串或字节对象来进一步控制每个部分的文件名、内容类型和附加头。元组应包含2到4个元素;文件名、内容(可选内容类型)和包含更多标题的可选字典。</p>
<p>我将使用以<code>None</code>为文件名的元组形式,以便从这些部分的请求中删除<code>filename="..."</code>参数:</p>
<pre><code>>>> files = {'foo': 'bar'}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--bb3f05a247b43eede27a124ef8b968c5
Content-Disposition: form-data; name="foo"; filename="foo"
bar
--bb3f05a247b43eede27a124ef8b968c5--
>>> files = {'foo': (None, 'bar')}
>>> print(requests.Request('POST', 'http://httpbin.org/post', files=files).prepare().body.decode('utf8'))
--d5ca8c90a869c5ae31f70fa3ddb23c76
Content-Disposition: form-data; name="foo"
bar
--d5ca8c90a869c5ae31f70fa3ddb23c76--
</code></pre>
<p><code>files</code>也可以是两个值元组的列表,如果您需要排序和/或具有相同名称的多个字段:</p>
<pre><code>requests.post(
'http://requestb.in/xucj9exu',
files=(
('foo', (None, 'bar')),
('foo', (None, 'baz')),
('spam', (None, 'eggs')),
)
)
</code></pre>
<p>如果同时指定<code>files</code>和<code>data</code>,则它取决于<code>data</code>的<em>值</em>将用于创建POST主体。如果<code>data</code>是一个字符串,则只使用它;否则将同时使用<code>data</code>和<code>files</code>,首先列出<code>data</code>中的元素。</p>
<p>还有一个优秀的<code>requests-toolbelt</code>项目,其中包括<a href="https://toolbelt.readthedocs.io/en/latest/uploading-data.html" rel="noreferrer">advanced Multipart support</a>。它接受与<code>files</code>参数格式相同的字段定义,但与<code>requests</code>不同的是,它默认不设置文件名参数。此外,它还可以流式处理来自open file对象的请求,其中<code>requests</code>将首先在内存中构造请求体:</p>
<pre><code>from requests_toolbelt.multipart.encoder import MultipartEncoder
mp_encoder = MultipartEncoder(
fields={
'foo': 'bar',
# plain file object, no filename or mime type produces a
# Content-Disposition header with just the part name
'spam': ('spam.txt', open('spam.txt', 'rb'), 'text/plain'),
}
)
r = requests.post(
'http://httpbin.org/post',
data=mp_encoder, # The MultipartEncoder is posted as data, don't use files=...!
# The MultipartEncoder provides the content-type header with the boundary:
headers={'Content-Type': mp_encoder.content_type}
)
</code></pre>
<p>字段遵循相同的约定;使用包含2到4个元素的元组来添加文件名、部分mime类型或额外的头。与<code>files</code>参数不同,如果不使用元组,则不会尝试查找默认的<code>filename</code>值。</p>