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

时间:2012-09-12 09:33:23

标签: python python-2.7 multipartform-data python-requests

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

11 个答案:

答案 0 :(得分:108)

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

>>> 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')), ('spam', (None, 'eggs'))))

如果同时指定filesdata,则它取决于data将用于创建POST正文的内容。如果data是一个字符串,则仅使用它;否则使用datafiles,首先列出data中的元素。

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

答案 1 :(得分:95)

由于之前的答案已经写好,因此请求已更改。有关更多详细信息,请查看bug thread at Github,并查看this comment示例。

简而言之,files参数采用dict,其中键是表单字段的名称,值是字符串或2,3或4长度的元组,如{{1}部分所述{3}}请求快速入门:

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

答案 2 :(得分:56)

当您不需要上传任何文件时,您需要使用files参数发送多部分表单POST请求甚至

来自原始requests来源:

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 or a 4-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)

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

<小时/> 如果此API不够pythonic,或者您需要发布多个具有相同名称的字段,请考虑使用requests toolbeltNone),它是core requests模块的扩展名它提供了对文件上传流的支持以及MultipartEncoder,它可以代替pip install requests_toolbelt使用,并且接受参数作为字典和元组。

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

答案 3 :(得分:4)

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

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)

完成后,文件成功上传

答案 4 :(得分:4)

发送多部分/表单数据键和值

curl命令:

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

python requests - More complicated POST requests

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

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

curl命令:

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 requests - POST a Multipart-Encoded File

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

仅此而已。

答案 5 :(得分: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)

答案 6 :(得分:1)

为阐明上述示例,

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

files = {}

不幸的是,它不起作用。

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

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 =

答案 7 :(得分:0)

以下是使用请求上传带有其他参数的单个文件的简单代码段:

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)

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

注意:想要对以上答案之一发表评论,但由于信誉不佳而无法发表评论,因此在此处起草了新的答复。

答案 8 :(得分:0)

我正在尝试使用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 =已发送参数

答案 9 :(得分:-1)

这是您需要上传一个大的单个文件作为多部分formdata的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');

答案 10 :(得分:-2)

邮递员生成的带有附加表单字段的文件上传代码:


Selection = [(8, '001'), (8, '00_1')]


# 1st option ----------------------------------------------------
A = np.zeros((1,1,5)); A[0,0,2]=8
for s in Selection:
    A = G(A, s)
print('Result is OK, but 1st method is NOT:'); print('A=',A); print()
#------------------------------------------------------------------

# 2nd option ----------------------------------------------------
A = np.zeros((1,1,5)); A[0,0,2]=8
GP = partial(G, A)
for s in Selection:
    A = GP(s)
print('Result is wrong and 2nd method, too:'); print('A=',A); print()
#------------------------------------------------------------------

# 3rd option ----------------------------------------------------
A = np.zeros((1,1,5)); A[0,0,2]=8
for s in Selection:
    GP = partial(G, A)
    A = GP(s)
print('Result is OK, but 3rd option is NOT:'); print('A=',A); print()
#------------------------------------------------------------------

# 4th option ----------------------------------------------------
A = np.zeros((1,1,5)); A[0,0,2]=8
GP = partial(G, A)
A = map(GP, Selection)
print('Result is NOT, but the 4th method is OK:'); print('A=',list(A)); print()
#------------------------------------------------------------------


# 5th option is MPI (calculated using multi-cores by multiprocessing module)
A = np.zeros((1,1,5)); A[0,0,2]=8
if __name__ =='__main__':
    pool=Pool()
    manager=Manager()
    A=manager.list(A)
    
    for i in range(2):
        GP = partial(G, A)
        GP_list = pool.map(GP, Selection)
        A=GP_list[-1]
    print('Result is NOT, but the 5th method is OK:'); print('A=',A); print()

    pool.close()
    pool.join()