我想发布多部分/表单数据编码数据。 我找到了一个可以做到这一点的外部模块:http://atlee.ca/software/poster/index.html 但我宁愿避免这种依赖。有没有办法使用标准库来做到这一点?
谢谢
这是一个旧线程,但仍然很受欢迎,所以这是我仅使用标准模块的贡献。
这个想法与here相同,但支持Python 2.x和Python 3.x。 它还具有一个主体生成器,以避免不必要的内存使用。
import codecs
import mimetypes
import sys
import uuid
try:
import io
except ImportError:
pass # io is requiered in python3 but not available in python2
class MultipartFormdataEncoder(object):
def __init__(self):
self.boundary = uuid.uuid4().hex
self.content_type = 'multipart/form-data; boundary={}'.format(self.boundary)
@classmethod
def u(cls, s):
if sys.hexversion < 0x03000000 and isinstance(s, str):
s = s.decode('utf-8')
if sys.hexversion >= 0x03000000 and isinstance(s, bytes):
s = s.decode('utf-8')
return s
def iter(self, fields, files):
"""
fields is a sequence of (name, value) elements for regular form fields.
files is a sequence of (name, filename, file-type) elements for data to be uploaded as files
Yield body's chunk as bytes
"""
encoder = codecs.getencoder('utf-8')
for (key, value) in fields:
key = self.u(key)
yield encoder('--{}\r\n'.format(self.boundary))
yield encoder(self.u('Content-Disposition: form-data; name="{}"\r\n').format(key))
yield encoder('\r\n')
if isinstance(value, int) or isinstance(value, float):
value = str(value)
yield encoder(self.u(value))
yield encoder('\r\n')
for (key, filename, fd) in files:
key = self.u(key)
filename = self.u(filename)
yield encoder('--{}\r\n'.format(self.boundary))
yield encoder(self.u('Content-Disposition: form-data; name="{}"; filename="{}"\r\n').format(key, filename))
yield encoder('Content-Type: {}\r\n'.format(mimetypes.guess_type(filename)[0] or 'application/octet-stream'))
yield encoder('\r\n')
with fd:
buff = fd.read()
yield (buff, len(buff))
yield encoder('\r\n')
yield encoder('--{}--\r\n'.format(self.boundary))
def encode(self, fields, files):
body = io.BytesIO()
for chunk, chunk_len in self.iter(fields, files):
body.write(chunk)
return self.content_type, body.getvalue()
演示
# some utf8 key/value pairs
fields = [('প্রায়', 42), ('bar', b'23'), ('foo', 'ން:')]
files = [('myfile', 'image.jpg', open('image.jpg', 'rb'))]
# iterate and write chunk in a socket
content_type, body = MultipartFormdataEncoder().encode(fields, files)
您无法使用 stdlib 快速完成此操作。 不过,请参阅此 PyMOTW 中的
MultiPartForm
类。 您可以使用或修改它来完成您需要的任何操作:
您可以使用python的标准库电子邮件模块。我不确定电子邮件模块何时获得此功能,但我测试此功能的 python 是
3.10.14
。
import email.parser
import email.mime.multipart
import email.mime.text
import email.mime.base
import mimetypes
import os
def encode_multipart(fields, files, charset=None):
multipart_data = email.mime.multipart.MIMEMultipart("form-data")
# Add form fields
for key, value in fields.items():
part = email.mime.text.MIMEText(str(value), "plain", _charset=charset)
part.add_header("Content-Disposition", f"form-data; name=\"{key}\"")
multipart_data.attach(part)
# Add files
for key, fp in files.items():
mimetype = mimetypes.guess_type(fp.name)[0]
maintype, subtype = mimetype.split("/", maxsplit=1)
basename = os.path.basename(fp.name)
part = email.mime.base.MIMEBase(maintype, subtype)
part.set_payload(fp.read())
part.add_header(
"Content-Disposition",
f"form-data; name=\"{key}\";filename=\"{basename}\""
)
email.encoders.encode_base64(part)
multipart_data.attach(part)
headerbytes, body = multipart_data.as_bytes().split(b"\n\n", 1)
hp = email.parser.BytesParser().parsebytes(headerbytes, headersonly=True)
return hp._headers, body
encode_multipart
将返回客户端可以发送到服务器的请求标头和请求 multipart/form-data
正文,您可以像这样使用它:
with open("<SOME-FILEPATH>") as fp:
fields = {
"foo": 1,
"bar": "two"
}
files = {
"file-key": fp
}
request_headers, request_body = encode_multipart(fields, files)
print(request_headers)
print(request_body)
我没有验证数据或处理任何错误,但这应该足以让人们开始。