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

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

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

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

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

Ответ 1

Chrome

Chrome (версия 38 с момента написания) имеет 3 способа определения типа MIME и делает это в определенном порядке. Ниже приведен фрагмент из файла src/net/base/mime_util.cc, method 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, но его закрытая природа источника затрудняет проверку.

Firefox

Как указано в коде 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, о котором сообщает браузер, является ненадежным.

Ответ 2

Кип, я потратил некоторое время на чтение 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. Мне все равно хотелось бы получить более авторитетную информацию об этом.

Ответ 3

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

Например:

HKEY_CLASSES_ROOT.zip - ContentType

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

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

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

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

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