2016-03-21

О MIME

Есть такой милый стандарт MIME. Если верить Википедии, произносится это как «майм». Но я почему-то всегда произносил это неправильно как «миме». Это — Multipurpose Internet Mail Extensions. Не путать с мимами — странными молчаливыми ребятами с чрезвычайно бледными лицами.
И вы постоянно сталкиваетесь с этими MIME сообщениями. Ибо это обычная электропочта, то самое, что посылается через SMTP, принимается через IMAP и ещё кучу протоколов и, в конце концов, отображается в вашей любимой почтовой программе. А ещё может быть сохранено в виде .eml или .msg файлов. Но на самом деле MIME встречается ещё чаще, ибо в HTTP что запрос, что ответ, по сути (за исключением первой строки с типом запроса и статусом ответа) являются MIME сообщениями.
MIME
MIME сообщения восходят ещё к тем временам, когда не было XML и JSON, и в Интернете правили текстовые протоколы. В MIME сообщении имеются заголовки, где название заголовка отделяется от значения двоеточием, пустая строка-разделитель, и тело сообщения.
Самым важным заголовком MIME является Content-Type.
Content-Type: text/plain
Значение этого заголовка — это тип содержимого сообщения. И здесь мы встречаемся с величайшим вкладом этого стандарта в Порядок во Вселенной: MIME type или MIME тип.
MIME тип состоит из двух слов, разделённых слэшем. Первое слово — тип содержимого. Текст — text. Изображение — image. Музыка — audio. Видосик — video. Это содержимое предназначено для обработки конкретной программой — application. Содержимое состоит из нескольких частей — multipart.
Второе слово — подтип содержимого. Просто текст без форматирования — text/plain, текст в HTML разметке — text/html. Изображение в JPEGimage/jpeg, в PNGimage/png. Аудио в MP3audio/mp3, в Ogg Vorbis, или Opus, или Speex — audio/ogg. Видео в MP4video/mp4, в Matroskavideo/x-matroska. Текст в формате OpenDocument, подразумевается, что открываться должен в соответствующих программах, — application/vnd.oasis.opendocument.text. PDF документ, должен открываться в программах для просмотра PDF, — application/pdf.
Типы MIME, по-хорошему, положено регистрировать в IANA. Часто при этом, если за данным форматом данных стоит конкретная организация, подтип начинается с vnd., например: application/vnd.ms-excel. Но, по устаревшим спецификациям, можно было не регистрировать подтипы, начинающиеся с x-. Они вроде как были экспериментальными, но некоторые так здорово прижились, что мы до сих пор имеем дело с ужастиками вроде application/x-www-form-urlencoded. Этот тип, кстати, обозначает один из способов отправки данных из HTML формы.
MIME types
Вообще, когда у нас есть какой-то кусок байт, хорошо, если файл, то мы можем определить его содержимое несколькими способами. Можно попытаться открыть это во всех имеющихся программах, авось какая-то и переварит. Очевидно, это не очень эффективно. Если у нас есть имя файла, то значит у этого имени есть расширение (по факту — буквы после последней точки), а расширение часто даёт понять, что это за файл. Так до сих пор поступают нынешние ОС, у них записано, файлы с каким расширением в какой программе открывать. Но здесь нужно имя файла, а его может не быть, если байты пришли по Сети или вообще хранятся где-то в базе данных.
Если же у нас есть MIME тип, то мы прекрасно знаем, что это за данные. А современные ОС ещё и знают, какой программой данный MIME тип открывать. Получается, что ОС хранят соответствие между программой и расширениями имени файлов, а также между программой и связанными с нею MIME типами.
MIME type associations
В серверостроении возникает немного другая проблема. У нас есть файл, и его надо передать по Сети (по HTTP). Чтобы сделать это правильно, нам нужно выдать MIME тип пересылаемых данных. К сожалению, ОС, как правило, не предоставляет механизма узнать MIME тип конкретного файла. Это банально нигде не хранится. Поэтому серверам приходится выводить тип из расширения имени файла, для этого есть даже специальные файлики-БД. Либо применять специальную магию. Очень часто тип файла можно узнать по нескольким первым байтам самого файла. Например, все изображения PNG начинаются с байт 89 50 4E 47, последние три байта — это буквы PNG в ASCII. Вот MIME тип и определяется по началу файла.
Мораль такова. MIME тип всегда передаётся по сети. MIME тип однозначно даёт знать, что это за данные, и что с ними можно сделать. А значит, если вы сохраняете байтики в базу данных, или ещё куда-нибудь, не потеряйте MIME тип, сохраните его рядышком. Потом это может очень пригодится.
А если вы создаёте свой формат данных, который может быть полезен за пределами данного запроса-ответа, который может быть стоит куда-то сохранить, придумайте для него свой MIME тип.
Trust Me
Особняком стоит тип multipart. С его помощью можно засовывать в одно сообщение несколько различных содержимых. Именно так создаются письма с вложениями.
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=part

--part
Content-Type: text/plain

This is a text message with PNG attachment.
--part
Content-Type: image/png
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=stackoverflow.png

iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABCElEQVQ4y7XRLUtEQRjF8Z/ulgG9
BrNpqy/NZhKDi/gFBIMoaJFpCmLSYvIWQQ0WNRj9CgbDCqIGwWw3TLlBRMsNC3tXr4gHhgnPM4f/
OUMNpRjG+s0G1dNlimH1LwZr2E8xtGobpBhWUwzzkOXFC/ZKkkZdgmecpBgOUgyNLC+O8Iad7qWB
H8obxTmGsIR3PGAxy4sONCsetbCL0ywvbtFOMWzhDsvYwAI6lQQphhGslMV94BgXmMQl5spOakWY
wTrauMZhlheP3TtVEW7wiqfybGOzxO9RVYSpEnccE+U9jPssL2b74p6dX31+9xsphumqvaoIn9/U
0kPcY5DlxYBfqFk3xr/pC6p9USZurgviAAAAAElFTkSuQmCC
--part--
Распаковать это безобразие можно, например, с помощью программы munpack.
Для типа multipart обязательно задаётся ещё и параметр boundary — разделитель частей сообщения. Заголовок Content-Transfer-Encoding указывает на то, как закодировано содержимое. Бинарные файлы в электропочте как правило кодируются в Base64, чтобы ограничиться при передаче исключительно ASCII символами. Что приводит к распуханию бинарных файлов в письме на треть. А вот в HTTP принято пересылать бинарные данные как есть.
Заголовок Content-Disposition указывает, как поступить с содержимым. Если attachment, значит, это отдельный файл, имя которого может быть задано тут же. В случае HTTP attachment означает, что браузер должен скачать файл, а не отобразить его в своём окне.
multipart сообщения (с разными boundary) могут быть вложены друг в друга. Получается целое дерево вложенных документов. Таким образом можно даже закодировать целый HTML со всеми сопутствующими картинками и стилями.
MIME multipart
Помните о MIME. Даже стандарты, которым уже больше тридцати лет, вполне себе существуют и работают где-то рядом. А MIME тип — это вообще штука, которая встречается на каждом шагу.