Определение MIME-типов файлов в PHP
MIME-типы в PHP
Наиболее эффективное решение: расширение Fileinfo (finfo)
Для надёжного определения MIME-типа файла в PHP рекомендуется использовать расширение Fileinfo. Оно анализирует сигнатуры (магические байты) содержимого, а не только расширение. Это гарантирует точность независимо от того, какое имя файла присвоено.
// Проверка наличия расширения
if (!extension_loaded('fileinfo')) {
echo 'Расширение fileinfo не загружено';
exit;
}
$filename = 'example.pdf';
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($filename);
echo $mime; // application/pdfPhp check type (проверка типа переменной в php)
Пошаговое объяснение
- new finfo(FILEINFO_MIME_TYPE) - создаёт объект Fileinfo с режимом возврата только MIME-типа (без кодировки).
- $finfo->file($filename) - анализирует указанный файл и возвращает строку MIME-типа.
Типичные проблемы и их решение
- Ошибка: "Class 'finfo' not found" - расширение fileinfo не установлено или не включено в php.ini.
Решение: Установите пакетphp-fileinfo(например,sudo apt install php-fileinfoдля Debian/Ubuntu) или раскомментируйте строкуextension=fileinfoв php.ini. - Пустой результат или false: Файл не существует или недоступен для чтения.
Решение: Перед вызовом проверьте существование файлаis_file()и права доступа.
Как определить MIME-тип с помощью устаревшей функции mime_content_type?
Функция mime_content_type() - более старый способ, основанный на расширении fileinfo (внутренне), но она объявлена устаревшей и может быть удалена в будущем. Для новых проектов лучше использовать finfo.
$mime = mime_content_type('document.docx');
echo $mime; // application/vnd.openxmlformats-officedocument.wordprocessingml.documentOf type string is deprecated php (предупреждение об устаревании типа string в php)
Проблема:
В некоторых окружениях (например, Windows) функция может возвращать application/octet-stream для неизвестных типов, а на Linux - точный тип. Это связано с отсутствием базы данных magic. Установка расширения fileinfo решает проблему.
Как определить MIME-тип по расширению файла без анализа содержимого?
Иногда требуется быстрая (но менее точная) оценка на основе имени файла. Подходит для случаев, когда анализ содержимого невозможен (например, файл удалён, но известно расширение).
function mimeByExtension($filename) {
$mimes = [
'txt' => 'text/plain',
'html' => 'text/html',
'jpg' => 'image/jpeg',
'png' => 'image/png',
'pdf' => 'application/pdf',
// ... другие типы
];
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
return $mimes[$ext] ?? 'application/octet-stream';
}
echo mimeByExtension('image.jpg'); // image/jpegPhp mime type (mime-типы в php)
Ошибки:
- Расширение может быть подделано (например, файл .exe переименован в .jpg) - MIME-тип будет неверным.
- Отсутствие соответствия в массиве - вернётся общий тип. Решение: дополнить массив всеми нужными расширениями.
Можно ли доверять MIME-типу из массива $_FILES?
При загрузке файла через форму PHP заполняет $_FILES['file']['type'] значением, переданным браузером. Это значение легко подделывается злоумышленником, так как оно берётся из HTTP-запроса.
// Ненадёжно!
$uploadedType = $_FILES['myfile']['type'];
if ($uploadedType !== 'image/png') {
exit('Только PNG разрешены');
}Проблема:
Браузер может отправить произвольный MIME-тип. Например, вредоносный файл с маскировкой под PNG.
Решение: Никогда не полагайтесь на $_FILES['file']['type'] для безопасности. Используйте finfo для проверки содержимого после загрузки.
Расширенные примеры работы с MIME-типами в PHP
1. Определение MIME-типа из строки или потока (без сохранения файла)
Если данные уже находятся в памяти (например, загружены из базы данных или сгенерированы на лету), можно использовать finfo->buffer().
$data = file_get_contents('somefile.jpg');
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->buffer($data);
echo $mime; // image/jpegimage/jpeg
2. Получение MIME-типа с кодировкой для текстовых файлов
По умолчанию FILEINFO_MIME_TYPE возвращает только тип. Константа FILEINFO_MIME вернёт строку с кодировкой.
$finfo = new finfo(FILEINFO_MIME);
echo $finfo->file('data.txt'); // text/plain; charset=utf-8text/plain; charset=utf-8
3. Пакетная обработка нескольких файлов
Один объект finfo можно использовать многократно для ускорения.
$files = ['a.jpg', 'b.png', 'c.pdf'];
$finfo = new finfo(FILEINFO_MIME_TYPE);
foreach ($files as $f) {
echo $f . ' => ' . $finfo->file($f) . PHP_EOL;
}a.jpg => image/jpeg b.png => image/png c.pdf => application/pdf
4. Определение MIME-типа для содержимого из базы данных (BLOB)
// Предположим, у нас есть BLOB-поле из MySQL
$blob = $row['file_data']; // бинарные данные
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->buffer($blob);
echo $mime;5. Обработка ошибок при отсутствии расширения fileinfo
if (!function_exists('finfo_open')) {
// Запасной вариант: mime_content_type()
$mime = mime_content_type($file);
} else {
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file);
}
if ($mime === false) {
// Ошибка: файл не найден или недоступен
$mime = 'application/octet-stream';
}
echo $mime;6. Сравнение производительности: finfo против mime_content_type
Выполним замер для 1000 файлов.
$files = glob('/var/www/html/*');
$start = microtime(true);
foreach ($files as $f) {
if (is_file($f)) {
mime_content_type($f);
}
}
$elapsed1 = microtime(true) - $start;
$start = microtime(true);
$finfo = new finfo(FILEINFO_MIME_TYPE);
foreach ($files as $f) {
if (is_file($f)) {
$finfo->file($f);
}
}
$elapsed2 = microtime(true) - $start;
echo "mime_content_type: $elapsed1 сек, finfo: $elapsed2 сек";Примерный вывод: mime_content_type: 0.045 сек, finfo: 0.038 сек
7. Определение MIME-типа для data URI
$dataUri = 'data:image/png;base64,iVBORw0KGgo...';
// Извлекаем только данные после запятой
$parts = explode(',', $dataUri, 2);
$raw = base64_decode($parts[1]);
$finfo = new finfo(FILEINFO_MIME_TYPE);
echo $finfo->buffer($raw); // image/pngimage/png
8. Работа с пользовательскими магическими файлами
Можно загрузить собственную базу сигнатур (magic.mgc).
$finfo = new finfo(FILEINFO_MIME_TYPE, '/path/to/custom.magic');
echo $finfo->file('unknown.dat');Примечание:
Если файл magic не найден, finfo автоматически использует системную базу. Ошибка возникает только при явном указании несуществующего файла.