如何在python中发送带有请求的"multipart/form-data"?

agr*_*huk 178 python multipartform-data python-2.7 python-requests

如何multipart/form-data在python中发送请求?如何发送文件,我理解,但如何通过这种方法发送表单数据无法理解.

Mar*_*ers 127

基本上,如果指定files参数(字典),requests则会发送multipart/form-dataPOST而不是application/x-www-form-urlencodedPOST.但是,您不限于使用该字典中的实际文件:

>>> import requests
>>> response = requests.post('http://httpbin.org/post', files=dict(foo='bar'))
>>> response.status_code
200
Run Code Online (Sandbox Code Playgroud)

和httpbin.org让你知道你发布的标题; 在response.json()我们有:

>>> 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'}
Run Code Online (Sandbox Code Playgroud)

更好的是,您可以通过使用元组而不是单个字符串或字节对象来进一步控制每个部件的文件名,内容类型和附加标头.元组预计包含2到4个元素; 文件名,内容,可选的内容类型和其他标题的可选字典.

我将使用元组形式None作为文件名,以便filename="..."从这些部分的请求中删除参数:

>>> 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--
Run Code Online (Sandbox Code Playgroud)

files 如果您需要订购和/或具有相同名称的多个字段,也可以是两值元组的列表:

requests.post(
    'http://requestb.in/xucj9exu',
    files=(
        ('foo', (None, 'bar')),
        ('foo', (None, 'baz')),
        ('spam', (None, 'eggs')),
    )
)
Run Code Online (Sandbox Code Playgroud)

如果同时指定filesdata,然后它取决于价值data东西将被用于创建POST体.如果data是字符串,则只使用它; 否则都datafiles被使用,与在元件中data首先列出.

还有一个优秀的requests-toolbelt项目,其中包括先进的Multipart支持.它采用与files参数相同格式的字段定义,但requests与之不同,它默认不设置文件名参数.此外,它可以从打开的文件对象流式传输请求,requests首先在内存中构造请求主体.

  • @zaki:的确,因为`multipart/form-data` Content-Type*必须*包含用于删除帖子正文中各部分的边界值.不设置`Content-Type`标头可确保`requests`将其设置为正确的值. (4认同)
  • 如果使用files = {},则不得使用headers = {'Content-Type':'blah blah'}! (3认同)
  • 重要提示:如果 `files=` 的值为 true,则请求只会作为 `multipart/form-data` 发送,因此如果您需要发送 `multipart/form-data` 请求但不包含任何文件,您可以设置一个真实但无意义的值,例如“{'':''}”,并在请求正文中设置“data=”。如果您这样做,请不要自己提供 `Content-Type` 标头;`requests` 将为您设置。您可以在这里查看真相:https://github.com/psf/requests/blob/2758124a13cddff7244b97b5ffe3fddabb90bc18/requests/models.py#L504 (3认同)

run*_*uhl 101

由于之前的答案已经写好,请求已经改变.有关更多详细信息,请查看Github上的错误线程,以及此注释的示例.

简而言之,files参数dict带有一个键,其中键是表单字段的名称,值是字符串或2,3或4长度的元组,如请求中的POST多段编码文件一节中所述快速开始:

>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
Run Code Online (Sandbox Code Playgroud)

在上面,元组组成如下:

(filename, data, content_type, headers)
Run Code Online (Sandbox Code Playgroud)

如果该值只是一个字符串,则文件名将与键相同,如下所示:

>>> files = {'obvius_session_id': '72c2b6f406cdabd578c5fd7598557c52'}

Content-Disposition: form-data; name="obvius_session_id"; filename="obvius_session_id"
Content-Type: application/octet-stream

72c2b6f406cdabd578c5fd7598557c52
Run Code Online (Sandbox Code Playgroud)

如果值是一个元组,第一个条目是Nonefilename属性将不包括在内:

>>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')}

Content-Disposition: form-data; name="obvius_session_id"
Content-Type: application/octet-stream

72c2b6f406cdabd578c5fd7598557c52
Run Code Online (Sandbox Code Playgroud)

  • 如果您需要区分`name`和`filename`但又有多个具有相同名称的字段怎么办? (2认同)

ccp*_*zza 62

即使您不需要上传任何文件,也需要使用该files参数发送多部分表单POST请求.

从原始请求来源:

def request(method, url, **kwargs):
    """Constructs and sends a :class:`Request <Request>`.

    ...
    :param files: (optional) Dictionary of ``'name': file-like-objects``
        (or ``{'name': file-tuple}``) for multipart encoding upload.
        ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``,
        3-tuple ``('filename', fileobj, 'content_type')``
        or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``,
        where ``'content-type'`` is a string
        defining the content type of the given file
        and ``custom_headers`` a dict-like object 
        containing additional headers to add for the file.
Run Code Online (Sandbox Code Playgroud)

相关部分是: file-tuple can be a2-tuple,.3-tupleor a4-tuple

基于以上所述,包含要上载的文件和表单字段的最简单的多部分表单请求如下所示:

multipart_form_data = {
    'file2': ('custom_file_name.zip', open('myfile.zip', 'rb')),
    'action': (None, 'store'),
    'path': (None, '/path1')
}

response = requests.post('https://httpbin.org/post', files=multipart_form_data)

print(response.content)
Run Code Online (Sandbox Code Playgroud)

注意None作为纯文本字段的元组中的第一个参数 - 这是文件名字段的占位符,仅用于文件上载,但对于文本字段传递,None因为第一个参数是必需的,以便提交数据.


如果这个API对你来说不够pythonic,或者你需要发布多个具有相同名称的字段,那么考虑使用请求toolbelt(pip install requests_toolbelt),它是核心请求模块的扩展,它提供对文件上传流的支持以及可以使用MultipartEncoder代替files和接受参数作为字典和元组.

MultipartEncoder可用于包含或不包含实际上载字段的多部分请求.必须将其分配给data参数.

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

multipart_data = MultipartEncoder(
    fields={
            # a file upload field
            'file': ('file.zip', open('file.zip', 'rb'), 'text/plain')
            # plain text fields
            'field0': 'value0', 
            'field1': 'value1',
           }
    )

response = requests.post('http://httpbin.org/post', data=multipart_data,
                  headers={'Content-Type': multipart_data.content_type})
Run Code Online (Sandbox Code Playgroud)

如果需要发送多个具有相同名称的字段,或者表单字段的顺序很重要,则可以使用元组或列表代替字典,即:

multipart_data = MultipartEncoder(
    fields=(
            ('action', 'ingest'), 
            ('item', 'spam'),
            ('item', 'sausage'),
            ('item', 'eggs'),
           )
    )
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用“None”代替空字符串。那么请求将根本不包含文件名。因此,不要使用 `Content-Disposition: form-data; 名称=“动作”;filename=""` 将会是 `Content-Disposition: form-data; 名称=“动作”`。这对我来说至关重要,因为服务器接受这些字段作为表单字段而不是文件。 (2认同)

Jai*_*nik 15

这是使用请求上传带有附加参数的单个文件的简单代码片段:

url = 'https://<file_upload_url>'
fp = '/Users/jainik/Desktop/data.csv'

files = {'file': open(fp, 'rb')}
payload = {'file_id': '1234'}

response = requests.put(url, files=files, data=payload, verify=False)
Run Code Online (Sandbox Code Playgroud)

请注意,您不需要明确指定任何内容类型。

注意:想对上述答案之一发表评论,但由于声誉低而无法评论,因此在此处起草了新回复。

  • 最不冗长且最容易理解。不管怎样,文件应该用“rb”选项“打开”吗? (2认同)

Chr*_*ris 12

通过在请求files中指定参数POSTContent-Type请求的 会自动设置为multipart/form-data(后跟boundary用于分隔多部分有效负载中每个正文部分的字符串),无论您仅发送files, 或同时发送form-data和(因此,应该\在这种情况下不要尝试手动设置)。然而,如果仅发送,则会自动设置为。files Content-Type form-dataContent-Typeapplication/x-www-form-urlencoded

\n

Content-Type您可以使用下面给出的示例打印出请求的标头来验证上述内容,该示例显示了如何上传具有(可选)相同的多个文件(或单个文件) key(即,\'files\'在下面的情况下),以及与可选form-data(即,data=data在下面的示例中)。有关如何POST单个和多个的文档可以分别在此处此处files找到。如果您需要上传大文件而不将其读入内存,请查看流式上传。\n对于服务器端\xe2\x80\x94如果需要的话\xe2\x80\x94请查看此答案,来自下面的代码片段已被采用,并且使用了FastAPI Web 框架。

\n

例子

\n
import requests\n\nurl = \'http://127.0.0.1:8000/submit\'\nfiles = [(\'files\', open(\'a.txt\', \'rb\')), (\'files\', open(\'b.txt\', \'rb\'))]\n#file = {\'file\': open(\'a.txt\',\'rb\')} # to send a single file\ndata ={"name": "foo", "point": 0.13, "is_accepted": False}\nr = requests.post(url=url, data=data, files=files) \nprint(r.json())\nprint(r.request.headers[\'content-type\'])\n
Run Code Online (Sandbox Code Playgroud)\n


Ski*_* Dz 8

您需要使用name站点 HTML 中的上传文件的属性。例子:

autocomplete="off" name="image">
Run Code Online (Sandbox Code Playgroud)

你看到了 name="image">吗?您可以在用于上传文件的站点的 HTML 中找到它。您需要使用它来上传文件Multipart/form-data

脚本:

import requests

site = 'https://prnt.sc/upload.php' # the site where you upload the file
filename = 'image.jpg'  # name example
Run Code Online (Sandbox Code Playgroud)

在这里,在图片的位置,在HTML中添加上传文件的名称

up = {'image':(filename, open(filename, 'rb'), "multipart/form-data")}
Run Code Online (Sandbox Code Playgroud)

如果上传需要点击上传按钮,可以这样使用:

data = {
     "Button" : "Submit",
}
Run Code Online (Sandbox Code Playgroud)

然后开始请求

request = requests.post(site, files=up, data=data)
Run Code Online (Sandbox Code Playgroud)

大功告成,文件上传成功


小智 5

import requests
# assume sending two files
url = "put ur url here"
f1 = open("file 1 path", 'rb')
f2 = open("file 2 path", 'rb')
response = requests.post(url,files={"file1 name": f1, "file2 name":f2})
print(response)
Run Code Online (Sandbox Code Playgroud)