MvG*_*MvG 9 python mime http multipartform-data amazon-s3
我正在编写一个脚本,使用RFC 2388中multipart/form-data定义的内容类型上传包含文件的内容.从长远来看,我正在尝试提供一个简单的Python脚本来为github上传二进制包,这涉及将类似表格的数据发送到Amazon S3.
这个问题已经问过如何做到这一点,但到目前为止它还没有得到公认的答案,而且目前这两个答案中更有用的是指出这些方法,这些方法反过来手动构建整个消息.我有点担心这种方法,特别是关于字符集和二进制内容.
还有这个问题,其目前得分最高的答案暗示了该MultipartPostHandler模块.但这与我提到的食谱没什么不同,因此我的担忧也适用于那些.
RFC 2388第4.3节明确规定除非另有声明,否则内容应为7位,因此可能需要Content-Transfer-Encoding标头.这是否意味着我必须对Base64编码二进制文件内容?或者Content-Transfer-Encoding: 8bit对于任意文件是否足够?或者应该读一下Content-Transfer-Encoding: binary?
一般的filename标题字段,特别是标题字段,默认情况下仅为ASCII.我希望我的方法能够传递非ASCII文件名.我知道,对于我目前为github上传内容的应用程序,我可能不需要它,因为文件名是在一个单独的字段中给出的.但我希望我的代码可以重用,所以我宁愿以一致的方式编码文件名参数.RFC 2388第4.4节建议RFC 2231中引入的格式,例如filename*=utf-8''t%C3%A4st.txt.
由于multipart/form-data本质上是一种MIME类型,我认为它应该是可以使用的email包从标准Python库撰写我的职务.特别是非ASCII头字段的相当复杂的处理是我想委托的.
所以我写了下面的代码:
#!/usr/bin/python3.2
import email.charset
import email.generator
import email.header
import email.mime.application
import email.mime.multipart
import email.mime.text
import io
import sys
class FormData(email.mime.multipart.MIMEMultipart):
def __init__(self):
email.mime.multipart.MIMEMultipart.__init__(self, 'form-data')
def setText(self, name, value):
part = email.mime.text.MIMEText(value, _charset='utf-8')
part.add_header('Content-Disposition', 'form-data', name=name)
self.attach(part)
return part
def setFile(self, name, value, filename, mimetype=None):
part = email.mime.application.MIMEApplication(value)
part.add_header('Content-Disposition', 'form-data',
name=name, filename=filename)
if mimetype is not None:
part.set_type(mimetype)
self.attach(part)
return part
def http_body(self):
b = io.BytesIO()
gen = email.generator.BytesGenerator(b, False, 0)
gen.flatten(self, False, '\r\n')
b.write(b'\r\n')
b = b.getvalue()
pos = b.find(b'\r\n\r\n')
assert pos >= 0
return b[pos + 4:]
fd = FormData()
fd.setText('foo', 'bar')
fd.setText('täst', 'Täst')
fd.setFile('file', b'abcdef'*50, 'Täst.txt')
sys.stdout.buffer.write(fd.http_body())
Run Code Online (Sandbox Code Playgroud)
结果如下:
--===============6469538197104697019==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: form-data; name="foo"
YmFy
--===============6469538197104697019==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: form-data; name*=utf-8''t%C3%A4st
VMOkc3Q=
--===============6469538197104697019==
Content-Type: application/octet-stream
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: form-data; name="file"; filename*=utf-8''T%C3%A4st.txt
YWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJj
ZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVm
YWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJj
ZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVm
YWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJj
ZGVmYWJjZGVmYWJjZGVm
--===============6469538197104697019==--
Run Code Online (Sandbox Code Playgroud)
它似乎确实很好地处理标头.二进制文件内容将获得base64编码,这可能是可以避免的,但它应该足够好.令我担心的是两者之间的文本字段.它们也是base64编码的.我认为根据标准,这应该足够好,但我宁愿在那里有明文,以防一些愚蠢的框架必须处理中间级别的数据,并且不知道Base64编码数据.
Content-Transfer-Encodingas 8bit或as binary?email.header包来格式化标题值?email.utils.encode_rfc2231这样做.)这些问题密切相关,可以概括为"你将如何实现这一点".在许多情况下,回答一个问题要么回答,要么废弃另一个问题.所以我希望你同意所有这些帖子都适合.
这是一个占位符答案,描述了我在等待某些问题的权威输入时所做的事情。如果它表明这种方法在至少一个设计决策中是错误或不合适的,我将很乐意接受不同的答案。
这是我现在用来根据我的喜好进行这项工作的代码。我做出了以下决定:
我可以在文本字段中使用 8 位数据并且仍然符合规范吗?
我决定这样做。至少对于这个应用程序来说,它确实有效。
我可以获得电子邮件包来将我的文本字段序列化为 8 位数据而无需额外编码吗?
我找不到办法,所以我正在做自己的连载,就像我在这方面看到的所有其他食谱一样。
我是否也可以避免对二进制文件内容进行 Base64 编码?
简单地以二进制形式发送文件内容似乎工作得很好,至少在我的单个应用程序中是这样。
如果我可以避免它,我应该将 Content-Transfer-Encoding 写为 8 位还是二进制?
正如RFC 2045 第 2.8 节所述,8bit数据受到 CRLF 对之间 998 个八位字节的行长度限制,我认为这binary是更通用的,因此这里的描述更合适。
如果我必须自己序列化正文,我如何单独使用 email.header 包来格式化标头值?
正如已经编辑到我的问题中的那样,email.utils.encode_rfc2231对此非常有用。我尝试首先使用 ascii 进行编码,但如果出现非 ascii 数据或双引号字符串内禁止的 ascii 字符,请使用该方法。
是否有一些实现已经完成了我想做的所有事情?
据我所知。不过,其他实现也被邀请采用我的代码中的想法。
编辑:
感谢这个评论,我现在意识到使用 RFC 2231 作为标头并没有被普遍接受:当前的 HTML 5 草案禁止使用它。人们还发现它在野外也会造成问题。但由于 POST 标头并不总是对应于特定的 HTML 文档(例如 Web API),因此我也不确定在这方面我是否会信任该草案。也许正确的方法是同时给出编码和未编码的名称,就像RFC 5987 第 4.2 节建议的那样。但该 RFC 适用于 HTTP 标头,而 multipart/form-data 标头在技术上是 HTTP 主体。因此,该 RFC 不适用,而且我不知道有任何 RFC 会明确允许(甚至鼓励)同时对多部分/表单数据使用两种表单。
| 归档时间: |
|
| 查看次数: |
6101 次 |
| 最近记录: |