Правильно отформатируйте multipart / form-data body
я пишу сценарий для загрузки материалов, включая файлы, используяmultipart/form-data
тип контента, определенный вRFC 2388, В конце концов яя пытаюсь предоставить простой скрипт на 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())
Результат выглядит так:
--===============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==--
Кажется, он довольно хорошо обрабатывает заголовки. Содержимое двоичного файла будет закодировано в base64, чего можно избежать, но которое должно работать достаточно хорошо. Что меня беспокоит, так это текстовые поля между ними. Они также закодированы в base64. Я думаю, что в соответствии со стандартом, это должно работать достаточно хорошо, но яЯ предпочел бы иметь простой текст, на тот случай, если какая-то тупая структура должна иметь дело с данными на промежуточном уровне и не знать о данных, закодированных в Base64.
ВопросыМогу ли я использовать 8-битные данные для своих текстовых полей и при этом соответствовать спецификации?Могу ли я получить пакет электронной почты для сериализации моих текстовых полей как 8-битных данных без дополнительной кодировки?Если мне нужно придерживаться какой-то 7-битной кодировки, могу ли я заставить реализацию использовать цитируемую печатную форму для тех частей текста, где эта кодировка короче, чем base64?Можно ли избежать кодирования base64 для содержимого двоичных файлов?Если я могу избежать этого, я должен написатьContent-Transfer-Encoding
как8bit
или как?binary
Если бы мне пришлось самому сериализовать тело, как я мог бы использоватьemail.header
пакет самостоятельно форматировать значения заголовка? (email.utils.encode_rfc2231
Является ли это.)Есть ли реализация, которая уже сделала все, что яя пытаюсь сделать?Эти вопросы очень тесно связаны и могут быть обобщены как «Как бы вы это реализовали?, Во многих случаях ответ на один вопрос либо отвечает, либо устаревает на другой. Поэтому я надеюсь, что вы согласны, что один пост для всех из них подходит.