如何在 python 中使用请求发送“multipart/form-data”?
- 2024-11-28 08:37:00
- admin 原创
- 9
问题描述:
如何在python中发送multipart/form-data
with requests
?如何发送文件,我明白了,但是如何通过这种方法发送表单数据就无法理解了。
解决方案 1:
基本上,如果您指定一个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'}
需要明确的是:使用参数时不应设置标头,而是保留它,因为它需要在标头中指定与请求正文中使用的值匹配的(唯一)边界值。Content-Type
`files`requests
更好的是,您可以使用元组而不是单个字符串或字节对象来进一步控制每个部分的文件名、内容类型和附加标头。元组预计包含 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')),
)
)
如果同时指定files
和data
,则取决于将使用什么值data
来创建 POST 正文。如果data
是字符串,则只使用它;否则同时使用data
和files
,其中首先列出元素data
。
还有一个优秀的requests-toolbelt
项目,它包括高级的 Multipart 支持。它采用与参数相同格式的字段定义files
,但与不同的是requests
,它默认不设置 filename 参数。此外,它可以从打开的文件对象流式传输请求,其中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
解决方案 2:
自从之前的一些答案写出来后,请求已经发生了变化。请查看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
文件名属性将不会被包含:
>>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')}
Content-Disposition: form-data; name="obvius_session_id"
Content-Type: application/octet-stream
72c2b6f406cdabd578c5fd7598557c52
解决方案 3:
即使您不需要上传任何文件,也需要使用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
可以是实际的文件对象,在处理纯文本字段时,它可以是字符串。
基于上述内容,包含要上传的文件和表单字段的最简单的多部分表单请求将如下所示:
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 特色,那么可以考虑使用请求工具带( ),它是核心请求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})
如果需要发送具有相同名称的多个字段,或者表单字段的顺序很重要,则可以使用元组或列表代替字典:
multipart_data = MultipartEncoder(
fields=(
('action', 'ingest'),
('item', 'spam'),
('item', 'sausage'),
('item', 'eggs'),
)
)
解决方案 4:
以下是使用请求上传带有附加参数的单个文件的简单代码片段:
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)
请注意,您不需要明确指定任何内容类型。
注意:想要对上述某个答案发表评论,但由于声誉低而无法发表评论,因此在这里起草了一份新的回复。
解决方案 5:
通过在请求中指定一个files
参数POST
,Content-Type
请求的 会自动设置为multipart/form-data
(后跟用于boundary
分隔多部分负载中每个主体部分的字符串),无论您是同时发送files
、 还是form-data
和files
(因此,在这种情况下不应尝试 Content-Type
手动设置)。然而,如果只form-data
发送 ,Content-Type
会自动设置为application/x-www-form-urlencoded
。
您可以使用下面给出的示例打印出Content-Type
请求的标头来验证上述内容,该示例展示了如何使用(可选)相同的方式key
(即'files'
下面的情况)以及可选的方式form-data
(即data=data
下面的示例)上传多个文件(或单个文件)。有关如何POST
单个和多个的文档可以分别在此处和此处files
找到。如果您需要上传大文件而不将它们读入内存,请查看流式上传。对于服务器端(如果需要),请查看此答案,下面的代码片段取自其中,并使用FastAPI Web 框架。
例子
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'])
解决方案 6:
您需要使用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)
完成,文件上传成功
解决方案 7:
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())
解决方案 8:
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)
解决方案 9:
发送 multipart/form-data 键和值
curl 命令:
curl -X PUT http://127.0.0.1:8080/api/xxx ...
-H 'content-type: multipart/form-data; boundary=----xxx' \n-F taskStatus=1
python请求-更复杂的 POST 请求:
updateTaskUrl = "http://127.0.0.1:8080/api/xxx"
updateInfoDict = {
"taskStatus": 1,
}
resp = requests.put(updateTaskUrl, data=updateInfoDict)
发送 multipart/form-data 文件
curl 命令:
curl -X POST http://127.0.0.1:8080/api/xxx ...
-H 'content-type: multipart/form-data; boundary=----xxx' \n-F file=@/Users/xxx.txt
python请求-POST 多部分编码文件:
filePath = "/Users/xxx.txt"
fileFp = open(filePath, 'rb')
fileInfoDict = {
"file": fileFp,
}
resp = requests.post(uploadResultUrl, files=fileInfoDict)
就这样。
解决方案 10:
为了澄清上面给出的例子,
“即使您不需要上传任何文件,您也需要使用文件参数来发送多部分表单 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)
:哦=
解决方案 11:
这里给出了很好的解释https://github.com/psf/requests/issues/1081
class ForceMultipartDict(dict):
def __bool__(self):
return True
FORCE_MULTIPART = ForceMultipartDict() # An empty dict that boolean-evaluates as `True`.
client.post("/", data={"some": "data"}, files=FORCE_MULTIPART)
解决方案 12:
以下是将一个大型单个文件作为多部分表单数据上传所需的 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');
解决方案 13:
这是通过多部分请求发送文件的一种方法
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)
我已经测试过两者,两者运行良好。
解决方案 14:
您可以使用urllib3.encode_multipart_formdata
。
from urllib3 import encode_multipart_formdata
import requests
data = [
('key1', 'value1'),
('key2', 'value2'),
]
request_body, content_type = encode_multipart_formdata(data)
requests.post(
url,
headers={'Content-Type': content_type},
data=request_body,
)
解决方案 15:
我正在尝试使用 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 = 已发送的参数
解决方案 16:
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'
'.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"))
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件