Как браузер определяет тип 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-файлов.

6 ответов

Решение

Хром

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, сообщаемый браузером, ненадежен.

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

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

Например:

HKEY_CLASSES_ROOT.zip - ContentType

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

HKEY_CLASSES_ROOT\Mime\ База данных \ Тип контента

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

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

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

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

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

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

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

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

Другие вопросы по тегам