Как браузер определяет MIME-тип загруженного файла?

87

У меня есть веб-приложение, в котором пользователю нужно загрузить файл .zip. На стороне сервера я проверяю MIME-тип загруженного файла, чтобы убедиться, что это application/x-zip-compressedили application/zip.

У меня это отлично сработало в Firefox и IE. Однако, когда его коллега протестировал его, в Firefox это не помогло (отправленный тип пантомимы был чем-то вроде " application/octet-stream"), но работал в Internet Explorer. Наши настройки кажутся идентичными: IE8, FF 3.5.1 со всеми отключенными надстройками, Win XP SP3, WinRAR установлен как собственный обработчик файлов .zip (не уверен, что это актуально).

Итак, мой вопрос: как браузер определяет, какой тип MIME отправлять?

Обратите внимание: я знаю, что тип mime отправляется браузером и, следовательно, ненадежен. Я просто проверяю это для удобства - в основном, чтобы дать более понятное сообщение об ошибке, чем те, которые вы получаете, пытаясь открыть не-zip-файл в виде zip-файла, и чтобы избежать загрузки (предположительно тяжелых) библиотек zip-файлов.

Кип
источник
application / octet-stream обозначает двоичный файл. Вы должны получить расширение файла, чтобы узнать, является ли это zip-файлом. Чтобы уточнить, сработало ли это для вас на FF, но не для вашего коллеги?
Кевин Кроуэлл,
да, у меня это сработало в обоих браузерах
Кип
взгляните на атрибуты input/@formenctypeилиform/@enctype
tuxSlayer

Ответы:

72

Хром

Chrome (версия 38 на момент написания) имеет 3 способа определения типа MIME и делает это в определенном порядке. Приведенный ниже фрагмент взят из файла src/net/base/mime_util.cc, метода MimeUtil::GetMimeTypeFromExtensionHelper.

// We implement the same algorithm as Mozilla for mapping a file extension to
// a mime type.  That is, we first check a hard-coded list (that cannot be
// overridden), and then if not found there, we defer to the system registry.
// Finally, we scan a secondary hard-coded list to catch types that we can
// deduce but that we also want to allow the OS to override.

Жестко запрограммированные списки находятся в файле немного раньше: https://cs.chromium.org/chromium/src/net/base/mime_util.cc?l=170 ( kPrimaryMappingsи kSecondaryMappings).

Пример: при загрузке файла CSV из системы Windows с установленным Microsoft Excel, Chrome сообщит об этом как application/vnd.ms-excel. Это связано с тем, что .csvон не указан в первом жестко запрограммированном списке, поэтому браузер возвращается к системному реестру. HKEY_CLASSES_ROOT\.csvимеет значение с именем Content Type, равным application/vnd.ms-excel.

Internet Explorer

Опять же, используя тот же пример, браузер сообщит application/vnd.ms-excel. Я думаю, что разумно предположить, что Internet Explorer (версия 11 на момент написания) использует реестр. Возможно, он также использует жестко запрограммированный список, такой как Chrome и Firefox, но его закрытый исходный код затрудняет проверку.

Fire Fox

Как указано в коде Chrome, Firefox (версия 32 на момент написания) работает аналогичным образом. Фрагмент из файла uriloader\exthandler\nsExternalHelperAppService.cpp, методnsExternalHelperAppService::GetTypeFromExtension

// OK. We want to try the following sources of mimetype information, in this order:
// 1. defaultMimeEntries array
// 2. User-set preferences (managed by the handler service)
// 3. OS-provided information
// 4. our "extras" array
// 5. Information from plugins
// 6. The "ext-to-type-mapping" category

Жестко запрограммированные списки находятся в файле раньше, где-то рядом со строкой 441. Вы ищете defaultMimeEntriesи extraMimeEntries.

В моем текущем профиле браузер сообщит об этом, text/csvпотому что для него есть запись mimeTypes.rdf(пункт 2 в списке выше). При новом профиле, в котором нет этой записи, браузер отправит отчет application/vnd.ms-excel(пункт 3 в списке).

Резюме

Жестко заданные списки в браузерах довольно ограничены. Часто тип MIME, отправляемый браузером, совпадает с типом, сообщенным ОС. И именно поэтому, как указано в вопросе, тип MIME, сообщаемый браузером, ненадежен.

user247702
источник
1
Благодарность! у вас есть ссылка на жестко запрограммированный список в исходном коде Chrome?
Кип
@Kip да, я добавил ссылку. У Firefox, похоже, нет (официального) онлайн-браузера исходного кода, мне пришлось загрузить его с их FTP-сервера.
user247702
Наличие MIME в качестве ms-excel для CSV раздражает, интересно, почему его нет в жестко заданном списке.
Крис
Было бы неплохо узнать, были ли какие-то обновления в обнаружении mime-типов с 2014 года.
Виталий Исаев
1
@VitalyIsaev, беглый взгляд на код Chrome показывает, что он не изменился с 2014 года.
user247702
12

Кип, я некоторое время читал RFC, MSDN и MDN. Вот что я смог понять. Когда браузер встречает файл для загрузки, он просматривает первый буфер данных, который он получает, а затем запускает на нем тест. Эти тесты пытаются определить, является ли файл известным типом MIME или нет, и, если известен тип MIME, он просто дополнительно проверит его на предмет того, какой из известных типов MIME и предпримет соответствующие действия. Я думаю, что IE пытается сначала сделать это, а не просто определять тип файла по расширению. На этой странице объясняется это для IE http://msdn.microsoft.com/en-us/library/ms775147%28v=vs.85%29.aspx . Что касается firefox, я понял, что он пытается прочитать информацию о файле из файловой системы или записи каталога, а затем определяет тип файла. Вот ссылка на FF https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIFile. Я все еще хотел бы получить более авторитетную информацию по этому поводу.

Кумар
источник
8

Вероятно, это зависит от ОС и, возможно, браузера, но в Windows тип MIME для данного расширения файла можно найти, заглянув в реестр в разделе HKCR:

Например:

HKEY_CLASSES_ROOT.zip - ContentType

Чтобы перейти от MIME к расширению файла, вы можете посмотреть ключи под

HKEY_CLASSES_ROOT \ Mime \ Database \ Тип содержимого

Чтобы получить расширение по умолчанию для определенного типа MIME.

Майкл А. Макклоски
источник
Спасибо. К сожалению, и для меня, и для моего коллеги это кажется правильным в нашем реестре. я думаю, поэтому у него это сработало в IE, но FF каким-то образом понимает это по-другому ... ну да ладно :(
Кип
5

Хотя это не ответ на ваш вопрос, он решает проблему, которую вы пытаетесь решить. YMMV.

Как вы писали, тип MIME ненадежен, поскольку каждый браузер определяет его по-своему. Однако браузеры отправляют исходное имя (включая расширение) файла. Итак, лучший способ справиться с проблемой - проверить расширение файла, а не тип MIME.

Если вам все еще нужен тип mime, вы можете использовать свой собственный apache mime.types, чтобы определить его на стороне сервера.

Johndodo
источник
1
Хотите уточнить? По моему опыту, браузеры всегда отправляют правильное исходное имя файла (с расширением), в то время как типы MIME сильно различаются. Так что да, я бы сказал, что это намного надежнее.
johndodo
Верный. Я хотел сказать, что конечный пользователь может установить любое расширение, независимо от фактического типа, поэтому ему не следует доверять.
Джизеус
Это правда, но не имеет значения, используете ли вы расширение или тип MIME - вы никогда не должны доверять вводимым пользователем данным. Но OP прямо заявил, что знает об этой проблеме, поэтому это не часть этого вопроса. Кстати, я был бы признателен, если бы вы удалили голос против (я предполагаю, что он исходил от вас).
johndodo
Вы правы, не обращал внимания на то, не в том вопрос, мое плохое. Я могу отменить свой голос, но для этого вам придется отредактировать ответ (принудительно) ...
Джизеус
Да, я согласен с джондодо. Как объяснил Стейн в своем ответе выше, Chrome и Firefox сначала проверяют расширение. В конце концов, они делают то же самое.
Jenix
0

Я согласен с johndodo, существует так много переменных, которые делают типы mime, отправляемые из браузеров, ненадежными. Я бы исключил полученные подтипы и сосредоточился только на таком типе, как «приложение». если ваше приложение основано на php, вы можете легко сделать это с помощью функции explode (). Кроме того, просто проверьте расширение файла, чтобы убедиться, что это .zip или любое другое сжатие, которое вы ищете!

Сеул Шахки
источник
0

Согласно rfc1867 - загрузка файла на основе формы в HTML :

Каждая часть должна быть помечена соответствующим типом контента, если тип носителя известен (например, выведен из расширения файла или информации о типе операционной системы) или как приложение / поток октетов.

Таким образом , мое понимание, application/octet-streamэто вроде как blanket catch-allидентификатор , если тип не может быть выведен .

smwikipedia
источник
да, я все это понимаю. вопрос был в том, как браузер делает вывод.
Кип
Но это стоит знать, правда? Если application/octet-streamэто уловка, то другим подходом было бы доверие браузеру, если он смог сделать предположение, и проведение собственных тестов на стороне сервера, если оно получилось application/octet-stream.
MikeBeaton,