如何在python中发送多部分/表单数据请求?怎么发文件,我懂,但是怎么用这种方法发表单数据就不懂了。


基本上,如果你指定了一个files参数(一个字典),那么请求将发送一个多部分的/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--

文件也可以是双值元组的列表,如果你需要排序和/或多个相同名称的字段:

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

如果同时指定了文件和数据,那么将使用什么来创建POST体取决于数据的值。如果data是字符串,则只使用它;否则,将同时使用数据和文件,首先列出数据中的元素。

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

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形参不同,如果不使用元组,则不会尝试查找默认的文件名值。


自从编写了以前的一些答案之后,请求已经发生了变化。在Github上看看这个问题的更多细节,这条评论是一个例子。

简而言之,files参数接受一个字典,键为表单字段的名称,值为字符串或2、3或4长度的元组,如请求快速入门中的POST一个多部分编码文件部分所述:

>>> 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,则filename属性将不包括在内:

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

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

72c2b6f406cdabd578c5fd7598557c52

即使不需要上传任何文件,也需要使用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可以是a:

2元组(文件名,fileobj) 3-tuple (filename, fileobj, content_type) 4元组(文件名,fileobj, content_type, custom_headers)。

\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \

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

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对你来说不够python化,那么可以考虑使用requests toolbelt (pip install requests_toolbelt),它是核心请求模块的扩展,提供了对文件上传流的支持,以及可以用来代替文件的MultipartEncoder,它还允许你将负载定义为字典、元组或列表。

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'),
           )
    )

您需要使用网站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

这里,在image的位置,用HTML添加上传文件的名称

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

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

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

然后启动请求

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

完成,文件上传成功


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

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

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)

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

注:想评论上述答案之一,但不能因为低声誉,所以起草了一个新的回应在这里。


发送multipart/form-data键和值

curl命令:

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命令:

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

POST一个多部分编码的文件:

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

这是所有。


我试图用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 =我们要发送数据的服务器 报头=发送的报头 file_data =发送的参数


为了澄清上面的例子,

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


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)

邮差生成的代码文件上传附加的表单字段:

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

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

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)

我都测试过了,两者都很好。


通过在POST请求中指定files参数,请求的Content-Type被自动设置为multipart/form-data(后面跟着用于分隔multipart负载中的每个主体部分的边界字符串),无论您只发送文件,还是同时发送form-data和文件(因此,在这种情况下,不应该尝试手动设置Content-Type)。然而,如果只发送表单数据,则Content-Type将自动设置为application/x-www-form-urlencoded。

You can print out the Content-Type header of the request to verify the above using the example given below, which shows how to upload multiple files (or a single file) with (optionally) the same key (i.e., 'files' in the case below), as well as with optional form-data (i.e., data=data in the example below). The documentation on how to POST single and multiple files can be found here and here, respectively. In case you need to upload large files without reading them into memory, have a look at Streaming Uploads. For the server side—in case this is needed—please have a look at this answer, from which the code snippet below has been taken, and which uses the FastAPI web framework.

import requests

url = 'http://127.0.0.1:8000/submit'
files = [('files', open('a.txt', 'rb')), ('files', open('b.txt', 'rb'))]
#file = {'file': open('a.txt','rb')} # to send a single file
data ={"name": "foo", "point": 0.13, "is_accepted": False}
r = requests.post(url=url, data=data, files=files) 
print(r.json())
print(r.request.headers['content-type'])

import json
import os
import requests
from requests_toolbelt import MultipartEncoder

AUTH_API_ENDPOINT = "http://localhost:3095/api/auth/login"

def file_upload(path_img, token ):
    url = 'http://localhost:3095/api/shopping/product/image'
    name_img = os.path.basename(path_img)

    mp_encoder = MultipartEncoder(
        fields={
            'email': 'mcm9@gmail.com',
            'source': 'tmall',
            'productId': 'product_0001',
            'image': (name_img, open(path_img, 'rb'), 'multipart/form-data')
        #'spam': ('spam.txt', open('spam.txt', 'rb'), 'text/plain'),
        }
    )

    head = {'Authorization': 'Bearer  {}'.format(token),
            'Content-Type': mp_encoder.content_type}

    with requests.Session() as s:
        result = s.post(url, data=mp_encoder, headers=head)

    return result

def do_auth(username, password, url=AUTH_API_ENDPOINT):
    data = {
        "email": username,
        "password": password
    }

    # sending post request and saving response as response object
    r = requests.post(url=url, data=data)

    # extracting response text
    response_text = r.text

    d = json.loads(response_text)
    # print(d)

    return d


if __name__ == '__main__':
    result = do_auth('mcm4@gmail.com','123456')
    token = result.get('data').get('payload').get('token')
    print(token)
    result = file_upload('/home/mcm/Pictures/1234.png',token)
    print(result.json())