313

如何在pythonmultipart/form-data中发送一个?requests如何发送文件,我明白,但是如何通过这种方法发送表单数据就无法理解。

4

13 回答 13

281

基本上,如果您指定一个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

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'}

更好的是,您可以通过使用元组而不是单个字符串或字节对象来进一步控制每个部分的文件名、内容类型和附加标头。元组预计包含 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--

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

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

如果同时指定filesdata,那么它取决于将用于创建 POST 正文的data如果data是字符串,则只会使用它;否则两者都使用data和,首先列出files的元素。data

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

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}
)

字段遵循相同的约定;使用具有 2 到 4 个元素的元组来添加文件名、部分 mime 类型或额外的标题。与参数不同,如果您不使用元组,则files不会尝试查找默认值。filename

于 2012-09-12T09:59:56.113 回答
123

自从写了一些以前的答案以来,请求已经发生了变化。请查看Github 上的此问题以获取更多详细信息,并查看此评论以获取示例。

简而言之,该files参数采用一个字典,其键是表单字段的名称,值是字符串或 2、3 或 4 长度的元组,如在请求中发布多部分编码文件部分所述快速开始:

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

在上面,元组的组成如下:

(filename, data, content_type, headers)

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

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

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

72c2b6f406cdabd578c5fd7598557c52

如果该值是一个元组并且第一个条目是None文件名属性,则将不包括在内:

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

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

72c2b6f406cdabd578c5fd7598557c52
于 2014-04-09T21:50:45.973 回答
110

即使您不需要上传任何文件,您也需要使用该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.

相关部分是:file-tuple can be a

  • 2-tuple文件名,文件对象
  • 3-tuple文件名、文件对象、内容类型
  • 4-tuple文件名、fileobj、content_type、custom_headers)。

可能不明显的是,它fileobj可以是处理文件时的实际文件对象,也可以是处理纯文本字段时的字符串。

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

import requests

multipart_form_data = {
    'upload': ('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)

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

多个同名字段

如果您需要发布多个具有相同名称的字段,那么您可以将有效负载定义为元组列表(或元组),而不是字典:

multipart_form_data = (
    ('file2', ('custom_file_name.zip', open('myfile.zip', 'rb'))),
    ('action', (None, 'store')),
    ('path', (None, '/path1')),
    ('path', (None, '/path2')),
    ('path', (None, '/path3')),
)

流式请求 API

如果上述 API 对您来说不够 Pythonic,那么请考虑使用requests 工具带( pip install requests_toolbelt),它是核心 requests模块的扩展,提供对文件上传流的支持以及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})

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

multipart_data = MultipartEncoder(
    fields=(
            ('action', 'ingest'), 
            ('item', 'spam'),
            ('item', 'sausage'),
            ('item', 'eggs'),
           )
    )
于 2016-03-13T18:29:29.113 回答
20

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

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)

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

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

于 2019-01-23T21:59:54.980 回答
8

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

autocomplete="off" name="image">

你看到了 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

在这里,在图片的地方,添加上传文件的 HTML 名称

up = {'image':(filename, open(filename, 'rb'), "multipart/form-data")}

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

data = {
     "Button" : "Submit",
}

然后开始请求

request = requests.post(site, files=up, data=data)

大功告成,文件上传成功

于 2018-05-01T11:29:02.263 回答
5

为了澄清上面给出的例子,

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

文件={}

不幸的是,不会工作。

您将需要输入一些虚拟值,例如

files={"foo": "bar"}

我在尝试将文件上传到 Bitbucket 的 REST API 时遇到了这个问题,不得不写下这个可憎的东西以避免可怕的“不支持的媒体类型”错误:

url = "https://my-bitbucket.com/rest/api/latest/projects/FOO/repos/bar/browse/foobar.txt"
payload = {'branch': 'master', 
           'content': 'text that will appear in my file',
           'message': 'uploading directly from python'}
files = {"foo": "bar"}
response = requests.put(url, data=payload, files=files)

:O=

于 2020-09-08T17:34:30.783 回答
5

发送 multipart/form-data 键和值

卷曲命令:

curl -X PUT http://127.0.0.1:8080/api/xxx ...
-H 'content-type: multipart/form-data; boundary=----xxx' \
-F taskStatus=1

python请求 - 更复杂的 POST 请求

    updateTaskUrl = "http://127.0.0.1:8080/api/xxx"
    updateInfoDict = {
        "taskStatus": 1,
    }
    resp = requests.put(updateTaskUrl, data=updateInfoDict)

发送多部分/表单数据文件

卷曲命令:

curl -X POST http://127.0.0.1:8080/api/xxx ...
-H 'content-type: multipart/form-data; boundary=----xxx' \
-F file=@/Users/xxx.txt

python请求 - 发布一个多部分编码的文件

    filePath = "/Users/xxx.txt"
    fileFp = open(filePath, 'rb')
    fileInfoDict = {
        "file": fileFp,
    }
    resp = requests.post(uploadResultUrl, files=fileInfoDict)

就这样。

于 2020-04-20T06:58:03.897 回答
4
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)
于 2020-12-24T21:59:15.877 回答
0

这是在多部分请求中发送文件的一种方法

import requests
headers = {"Authorization": "Bearer <token>"}
myfile = 'file.txt'
myfile2 = {'file': (myfile, open(myfile, 'rb'),'application/octet-stream')}
url = 'https://example.com/path'
r = requests.post(url, files=myfile2, headers=headers,verify=False)
print(r.content)

其他方法

import requests

url = "https://example.com/path"

payload={}
files=[
  ('file',('file',open('/path/to/file','rb'),'application/octet-stream'))
]
headers = {
  'Authorization': 'Bearer <token>'
}

response = requests.request("POST", url, headers=headers, data=payload, files=files)

print(response.text)

我已经测试了两者,都可以正常工作。

于 2021-11-26T12:10:44.080 回答
0

这是您需要将一个大的单个文件作为多部分表单数据上传的 python 片段。在服务器端运行 NodeJs Multer 中间件。

import requests
latest_file = 'path/to/file'
url = "http://httpbin.org/apiToUpload"
files = {'fieldName': open(latest_file, 'rb')}
r = requests.put(url, files=files)

对于服务器端,请查看 multer 文档:https ://github.com/expressjs/multer 这里的字段 single('fieldName') 用于接受一个文件,如下所示:

var upload = multer().single('fieldName');
于 2018-10-04T15:58:51.543 回答
0

通过在请求中指定files参数,将请求的 设置为(后跟参数),无论您是只发送,还是同时发送数据。然而,如果只发送数据,则设置为。POSTcontent-typemultipart/form-databoundaryfilesformfilesformcontent-typeapplication/x-www-form-urlencoded

您可以在下面给出的示例中打印出content-type标题以验证上述内容,该示例显示了如何上传具有(可选)相同key(即,files在下面的情况下)以及可选form数据(即,data=form_data)。对于服务器端——如果你需要的话——请看一下这个答案,下面的代码片段是从中获取的,它使用了 FastAPI Web 框架。

import requests

url = 'http://127.0.0.1:8000/submit'
files = [('files', open('test_files/a.txt', 'rb')), ('files', open('test_files/b.txt', 'rb'))]
form_data ={"name": "foo", "point": 0.13, "is_accepted": False}
resp = requests.post(url=url, data=form_data, files=files) 
print(resp.json())
print(resp.request.headers['content-type'])
于 2022-01-09T13:29:28.227 回答
-1

我正在尝试使用 python 3 中的请求模块向 URL_server 发送请求。这对我有用:

# -*- coding: utf-8 *-*
import json, requests

URL_SERVER_TO_POST_DATA = "URL_to_send_POST_request"
HEADERS = {"Content-Type" : "multipart/form-data;"}

def getPointsCC_Function():
  file_data = {
      'var1': (None, "valueOfYourVariable_1"),
      'var2': (None, "valueOfYourVariable_2")
  }

  try:
    resElastic = requests.post(URL_GET_BALANCE, files=file_data)
    res = resElastic.json()
  except Exception as e:
    print(e)

  print (json.dumps(res, indent=4, sort_keys=True))

getPointsCC_Function()

在哪里:

  • URL_SERVER_TO_POST_DATA = 我们要发送数据的服务器
  • HEADERS = 发送的标头
  • file_data = 发送的参数
于 2020-08-26T23:56:17.073 回答
-3

Postman 为文件上传生成了带有附加表单字段的代码:

import http.client
import mimetypes
from codecs import encode

conn = http.client.HTTPSConnection("data.XXXX.com")
dataList = []
boundary = 'wL36Yn8afVp8Ag7AmP8qZ0SA4n1v9T'
dataList.append(encode('--' + boundary))
dataList.append(encode('Content-Disposition: form-data; name=batchSize;'))

dataList.append(encode('Content-Type: {}'.format('text/plain')))
dataList.append(encode(''))

dataList.append(encode("1"))
dataList.append(encode('--' + boundary))
dataList.append(encode('Content-Disposition: form-data; name=file; filename={0}'.format('FileName-1.json')))

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

with open('FileName-1.json', 'rb') as f:
  dataList.append(f.read())
dataList.append(encode('--'+boundary+'--'))
dataList.append(encode(''))
body = b'\r\n'.join(dataList)
payload = body
headers = {
  'Cookie': 'XXXXXXXXXXX',
  'Content-type': 'multipart/form-data; boundary={}'.format(boundary)
}
conn.request("POST", "/fileupload/uri/XXXX", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
于 2021-05-20T22:09:50.530 回答