Мне просто интересно, как большинство людей извлекают MIME-тип из файла на Java? Пока что пробовал две утилиты: JMimeMagic и Mime-Util.
Первый дал мне исключения памяти, второй не закрывает свои потоки должным образом. Мне просто было интересно, есть ли у кого-нибудь еще метод / библиотека, которые они использовали и правильно работали?
Я использовал класс, который был размещен здесь в качестве ответа: stackoverflow.com/a/10140531/293280
Теперь ответом должна быть Тика. Другие ответы ниже освещают многие зависимости от Tika, но я не вижу их с tika-core.
@ javamonkey79, когда мы используем TIka, он закрывает файл, и его больше нельзя использовать. String contentType = tika.detect (is).




К несчастью,
mimeType = file.toURL().openConnection().getContentType();
не работает, поскольку такое использование URL-адреса оставляет файл заблокированным, так что, например, его невозможно удалить.
Однако у вас есть это:
mimeType= URLConnection.guessContentTypeFromName(file.getName());
а также следующее, преимущество которого заключается в том, что оно выходит за рамки простого использования расширения файла, и позволяет взглянуть на контент
InputStream is = new BufferedInputStream(new FileInputStream(file));
mimeType = URLConnection.guessContentTypeFromStream(is);
//...close stream
Однако, как следует из комментария выше, встроенная таблица mime-типов весьма ограничена, не включая, например, MSWord и PDF. Итак, если вы хотите обобщить, вам нужно выйти за рамки встроенных библиотек, используя, например, Mime-Util (отличная библиотека, использующая как расширение файла, так и содержимое).
Отличное решение - мне очень помогло! Обертывание FileInputStream в BufferedInputStream является важной частью - иначе guessContentTypeFromStream вернет null (переданный экземпляр InputStream должен поддерживать метки)
Однако URLConnection имеет очень ограниченный набор типов контента, которые он распознает. Например, он не может обнаружить application/pdf.
@kpentchev он обнаруживает pdf для меня. Но он не обнаруживает офисные файлы, например * .doc
Он остается заблокированным только потому, что вы не оставили себе возможности закрыть его. Отключение URLConnection разблокирует его.
как guessContentTypeFromStream, так и guessContentTypeFromName НЕ распознают, например. mp4
guessContentTypeFromName() использует файл $JAVA_HOME/lib/content-types.properties по умолчанию. вы можете добавить свой собственный расширенный файл, изменив системное свойство System.setProperty("content.types.user.table","/lib/path/to/your/property/file");
Он не обнаруживает файлы .js, .css. Есть ли другой способ обнаружить эти файлы?
Есть ссылки на Mime-Util ??? Я нашел проект в github, но он не содержит описания :(
guessContentTypeFromName использует этот synchronized FileNameMap getFileNameMap удачи в многопоточности
JAF API является частью JDK 6. Посмотрите на пакет javax.activation.
Наиболее интересными классами являются javax.activation.MimeType - фактический держатель типа MIME - и javax.activation.MimetypesFileTypeMap - класс, экземпляр которого может разрешать тип MIME как String для файла:
String fileName = "/path/to/file";
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
// only by file name
String mimeType = mimeTypesMap.getContentType(fileName);
// or by actual File instance
File file = new File(fileName);
mimeType = mimeTypesMap.getContentType(file);
К сожалению, как указано в javadoc для getContentType(File): Возвращает MIME-тип файлового объекта. Реализация в этом классе вызывает getContentType(f.getName()).
И помните, что вы можете расширить эту функциональность с помощью файла META-INF / mime.types, поэтому он идеален, если вы вынуждены использовать Java 6. docs.oracle.com/javaee/5/api/javax/activation/…
вы можете пропустить создание нового объекта с помощью MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file)
Спасибо за Ваш ответ. У меня это успешно работает.
Но он по-прежнему возвращает тип содержимого только на основе имени файла. А это особенно опасно для файлов, загружаемых пользователями.
Это не работает, например, для файлов pdf (возвращается application/octet-stream).
От розаиндия:
FileNameMap fileNameMap = URLConnection.getFileNameMap();
String mimeType = fileNameMap.getContentTypeFor("alert.gif");
Кто бы ни проголосовал за ответ, пожалуйста, добавьте комментарий, чтобы я (и другие) могли научиться публиковать лучшие ответы.
Я не голосовал за вас, но getFileNameMap не работает для многих базовых типов файлов, например «bmp». Также URLConnection.guessContentTypeFromName возвращает то же самое
Очень неполная функция. Начиная с Java 7, расширения html, pdf и jpeg возвращают правильный mime-тип, но js и css возвращают null!
Я тестировал с помощью «webm», и он вернул ноль.
В Java 7 теперь можно просто использовать Files.probeContentType(path).
Это было очень полезно, поскольку веб-сайт mime-util, похоже, не работает, и я не могу сказать, поддерживается ли вообще библиотека!
Это работает хорошо, однако я не нашел способа добавить другие типы файлов, которые я понимаю. Например, образ ISO возвращает null, как и архив .zip, и даже файл конфигурации ini.
Это прекрасное.
@ james.garriss, и он принес мне больше очков, чем любой другой ответ, который я когда-либо давал! Сумасшедший, да? :)
Имейте в виду, что Files.probeContentType (Path) содержит ошибки в нескольких операционных системах, и было отправлено множество отчетов об ошибках. У меня была проблема с программным обеспечением, работающим на ubuntu, но не работающим на Windows. Казалось, что в Windows Files.probeContentType (Path) всегда возвращал null. Это была не моя система, поэтому я не проверял версию JRE или Windows. Это были Windows 7 или 8, вероятно, с Oracle JRE для Java 7.
Я работаю на OS X 10.9 и получаю null для файлов .xml, .png и .xhtml. Не знаю, делаю ли я что-то ужасно неправильно, но это кажется ужасным.
Мне не удалось заставить это работать успешно, если файл не имеет расширения.
Похоже, что, по крайней мере, в * nix-подобных системах детектор типа файла по умолчанию просто возвращает null, и нужно вручную добавить одну или несколько реализаций детектора, что, похоже, не слишком прямолинейно. Так что, по крайней мере, для моего варианта использования, который представляет собой простой метод сопоставления расширения файла с типом mime, это решение не работает.
Основное ограничение заключается в том, что файл должен существовать в файловой системе. Это не работает с потоком, байтовым массивом и т. д.
Что еще более странно, у меня есть два ноутбука с Windows 8.1, один из которых получает application/x-zip-compressed, а другой - null в результате вызова этого в zip-файле. Совершенно ненадежно: \. Итак, учитывая, что я хочу, чтобы мое приложение включало схему кодирования файла (допустим, мое приложение принимает конфигурацию как XML, так и JSON), а файл просто называется «конфигурацией» (без расширения), это самый надежный способ определить тип этого файла, обман и чтение нескольких байтов?
этот метод не может возвращать тип mime, когда я удаляю расширение из имени. Например, если имя test.mp4, я меняю его на «test», и метод возвращает null. Также я меняю расширение фильма на png и т. д. он возвращает тип mime png
Это бесполезно, если у файла отсутствует или неправильное расширение.
Реализация на основе Linux, похоже, использует Linux / usr / bin / file, что хорошо, если нет расширения, которому он просто верит, не глядя глубже, что плохо. Если вы переименуете XML-файл в .json, это скажет вам, что это JSON. Мусор на входе, мусор на выходе. Вы просто не хотите доверять этому подходу, если вы не уверены в своих файловых данных.
@RussBateman unless there's an extension, which it just believes without looking deeper, which is bad. Делают ли nginx / apache и т. д. Больше, чем просто смотрят на расширение?
Этот метод плохой, он отключается от расширения, не беспокоится о магическом числе и возвращает null в разных системах ... даже с той же ОС. Был обманут и не могу рекомендовать. Вы также можете просто сравнить расширение файла.
А чтобы получить Path от String, используйте Paths.get(str).
В Windows для определения типа файла используются только расширения. В Linux до Java 8 использовалось множество детекторов: детекторы на основе содержимого, основанные на библиотеках Gnome I / O, Gnome VFS и libmagic, а также на основе расширений, реализованных на /etc/mime.types. Но начиная с Java9 все детекторы на основе содержимого были удалены из JDK (12), а для Linux остались только детекторы на основе расширений. Поэтому, если у вашего файла нет расширения, этот метод всегда будет возвращать null :(
Я пробовал несколько способов сделать это, в том числе первый, который сказал @Joshua Fox. Но некоторые не распознают частые mimetypes, например, для файлов PDF, а другим нельзя доверять с поддельными файлами (я пробовал с файлом RAR с расширением, измененным на TIF). Решение, которое я нашел, как также поверхностно сказал @Joshua Fox, заключается в использовании MimeUtil2, например:
MimeUtil2 mimeUtil = new MimeUtil2();
mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
String mimeType = MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(file)).toString();
У меня вообще не было успеха с MimeUtil2 - почти все возвращалось как application / octet-stream. Я использовал MimeUtil.getMimeTypes () с гораздо большим успехом после инициализации с помощью `MimeUtil.registerMimeDetector (" eu.medsea.mimeutil.detector.M agicMimeMimeDetector "); MimeUtil.registerMimeDetector ("eu.medsea.mimeutil.detector.E xtensionMimeDetector"); MimeUtil.registerMimeDetector ("eu.medsea.mimeutil.detector.O pendesktopMimeDetect или"); `
Спасибо за рабочее решение. В документации mime-util не очень ясно, как создать экземпляр служебного класса. Наконец он заработал, но заменил строку имени класса фактическим классом. MimeUtil.registerMimeDetector (ExtensionMimeDetector.class.ge tName ()); String mimeType = MimeUtil.getMostSpecificMimeType (MimeUtil.getMimeTypes (filen file ame)). ToString ();
Если вы разработчик Android, вы можете использовать служебный класс android.webkit.MimeTypeMap, который сопоставляет типы MIME с расширениями файлов и наоборот.
Следующий фрагмент кода может вам помочь.
private static String getMimeType(String fileUrl) {
String extension = MimeTypeMap.getFileExtensionFromUrl(fileUrl);
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
Это также работает, если попытаться использовать локальные пути к файлам, такие как "/sdcard/path/to/video.extension". Проблема в том, что если локальный файл содержит пробел на своем пути, он всегда возвращает null
Апач Тика предлагает в тика-ядро определение типа mime на основе магических маркеров в префиксе потока. tika-core не извлекает другие зависимости, что делает его таким же легким, как неподдерживаемый в настоящее время Утилита обнаружения типа Mime.
Пример простого кода (Java 7) с использованием переменных theInputStream и theFileName
try (InputStream is = theInputStream;
BufferedInputStream bis = new BufferedInputStream(is);) {
AutoDetectParser parser = new AutoDetectParser();
Detector detector = parser.getDetector();
Metadata md = new Metadata();
md.add(Metadata.RESOURCE_NAME_KEY, theFileName);
MediaType mediaType = detector.detect(bis, md);
return mediaType.toString();
}
Обратите внимание, что MediaType.detect(...) нельзя использовать напрямую (ТИКА-1120). Дополнительные подсказки представлены в https://tika.apache.org/1.24/detection.html.
+1 Также Metadata.RESOURCE_NAME_KEY можно не указывать (если у вас его нет или вы не можете полагаться на оригинальное имя), но в этом случае вы получите неверный результат в некоторых случаях (например, в офисных документах).
У него есть некоторые проблемы с обнаружением XLSX, если для имени файла нет расширения ... но это решение простое и элегантное.
I was just wondering how most people fetch a mime type from a file in Java?
Я опубликовал свой пакет Java SimpleMagic, который позволяет определять тип содержимого (тип mime) из файлов и массивов байтов. Он предназначен для чтения и запуска командных магических файлов файла Unix (1), которые являются частью большинства конфигураций ОС Unix.
Я пробовал Apache Tika, но это огромный с множеством зависимостей, URLConnection не использует байты файлов, а MimetypesFileTypeMap также просто смотрит на имена файлов.
С SimpleMagic вы можете делать что-то вроде:
// create a magic utility using the internal magic file
ContentInfoUtil util = new ContentInfoUtil();
// if you want to use a different config file(s), you can load them by hand:
// ContentInfoUtil util = new ContentInfoUtil("/etc/magic");
...
ContentInfo info = util.findMatch("/tmp/upload.tmp");
// or
ContentInfo info = util.findMatch(inputStream);
// or
ContentInfo info = util.findMatch(contentByteArray);
// null if no match
if (info != null) {
String mimeType = info.getMimeType();
}
Протестировал на нескольких файлах изображений. Все расширения переименованы. Ваша замечательная библиотека справилась с этим должным образом. Конечно, тоже свет :).
Да, это хорошо работает. А для тех, кому нужно использовать это решение в Android, вы можете просто включить в файл build.gradle следующее: compile ('com.j256.simplemagic: simplemagic: 1.10')
Это отличное решение! Спасибо!
Если вы застряли с java 5-6, затем этот служебный класс из сервой продукт с открытым исходным кодом.
Вам нужна только эта функция
public static String getContentType(byte[] data, String name)
Он проверяет первые байты содержимого и возвращает типы содержимого на основе этого содержимого, а не по расширению файла.
Работал с простыми, популярными и немногими нужными мне типами файлов :)
Для загрузки файлов лучше использовать двухуровневую проверку.
Сначала вы можете проверить mimeType и подтвердить его.
Во-вторых, вы должны преобразовать первые 4 байта вашего файла в шестнадцатеричное, а затем сравнить их с магическими числами. Тогда это будет действительно безопасный способ проверки файлов.
если вы работаете в ОС linux, есть командная строка file --mimetype:
String mimetype(file){
//1. run cmd
Object cmd=Runtime.getRuntime().exec("file --mime-type "+file);
//2 get output of cmd , then
//3. parse mimetype
if (output){return output.split(":")[1].trim(); }
return "";
}
потом
mimetype("/home/nyapp.war") // 'application/zip'
mimetype("/var/www/ggg/au.mp3") // 'audio/mp3'
Это будет работать, но IMO - плохая практика, поскольку он связывает ваш код с конкретной ОС и требует, чтобы внешняя утилита присутствовала в системе, в которой она запущена. Не поймите меня неправильно; это полностью действующее решение, но оно нарушает переносимость - что является одной из основных причин использовать Java в первую очередь ...
@ToVine: Для протокола, я с уважением не согласен. Не каждая программа на Java обязана быть переносимой. Позвольте контексту и программисту принять это решение. en.wikipedia.org/wiki/Java_Native_Interface
весной файл MultipartFile;
org.springframework.web.multipart.MultipartFile
file.getContentType();
Попробовав различные другие библиотеки, я остановился на mime-util.
<groupId>eu.medsea.mimeutil</groupId>
<artifactId>mime-util</artifactId>
<version>2.1.3</version>
</dependency>
File file = new File("D:/test.tif");
MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
Collection<?> mimeTypes = MimeUtil.getMimeTypes(file);
System.out.println(mimeTypes);
public String getFileContentType(String fileName) {
String fileType = "Undetermined";
final File file = new File(fileName);
try
{
fileType = Files.probeContentType(file.toPath());
}
catch (IOException ioException)
{
System.out.println(
"ERROR: Unable to determine file type for " + fileName
+ " due to exception " + ioException);
}
return fileType;
}
Этот метод Files.probeContentType (String) доступен, начиная с версии JDK 1.7, и у меня он очень хорошо работает.
Спасибо, только я не могу понять, почему некоторые пользователи проголосовали против)))
Совсем нет, может у них более ранняя версия JDK :)))
С Апач Тика вам понадобится только три строки кода:
File file = new File("/path/to/file");
Tika tika = new Tika();
System.out.println(tika.detect(file));
Если у вас отличная консоль, просто вставьте и запустите этот код, чтобы поиграть с ней:
@Grab('org.apache.tika:tika-core:1.14')
import org.apache.tika.Tika;
def tika = new Tika()
def file = new File("/path/to/file")
println tika.detect(file)
Имейте в виду, что его API-интерфейсы богаты, он может разбирать «все». Начиная с tika-core 1.14, у вас есть:
String detect(byte[] prefix)
String detect(byte[] prefix, String name)
String detect(File file)
String detect(InputStream stream)
String detect(InputStream stream, Metadata metadata)
String detect(InputStream stream, String name)
String detect(Path path)
String detect(String name)
String detect(URL url)
См. апидоки для получения дополнительной информации.
Это не работает для csv. wtf? stackoverflow.com/questions/46960231/…
Одна плохая вещь в Тике - множество зависимостей. Это увеличило размер моей банки на 54 МБ !!!
@helmyTika 1.17 является автономным и имеет размер всего 648 КБ.
... или просто new Tika().detect(file.toPath()) для обнаружения на основе расширения файла, а не обнаружения на основе содержимого файла
Документы @ Lu55 говорят, что по-прежнему использует содержимое документа. Я думаю, вы имеете в виду new Tika().detect(file.getPath()), который использует только расширение файла
Это самый простой способ, который я нашел для этого:
byte[] byteArray = ...
InputStream is = new BufferedInputStream(new ByteArrayInputStream(byteArray));
String mimeType = URLConnection.guessContentTypeFromStream(is);
Отличное решение!
Чтобы скинуть свои 5 центов:
TL, DR
Я использую MimetypesFileTypeMap и добавляю любой mime, которого нет, и он мне особенно нужен, в файл mime.types.
А теперь длинное прочтение:
Прежде всего, список типов MIME - огромный, см. Здесь: https://www.iana.org/assignments/media-types/media-types.xhtml
Мне нравится сначала использовать стандартные средства, предоставляемые JDK, и если это не сработает, я пойду и поищу что-нибудь еще.
Определить тип файла по расширению файла
Начиная с версии 1.6, Java имеет MimetypesFileTypeMap, как указано в одном из ответов выше, и это самый простой способ определить тип mime:
new MimetypesFileTypeMap().getContentType( fileName );
В своей ванильной реализации это не так много (то есть работает для .html, но не для .png). Однако добавить любой тип контента, который вам может понадобиться, очень просто:
Примеры записей для файлов png и js:
image/png png PNG
application/javascript js
Подробнее о формате файла mime.types см. Здесь: https://docs.oracle.com/javase/7/docs/api/javax/activation/MimetypesFileTypeMap.html
Определить тип файла по содержимому файла
Начиная с версии 1.7 в Java есть java.nio.file.spi.FileTypeDetector, который определяет стандартный API для определения типа файла в конкретный способ реализации.
Чтобы получить тип MIME для файла, вы должны просто использовать Файлы и сделать это в своем коде:
Files.probeContentType(Paths.get("either file name or full path goes here"));
Определение API предоставляет средства, которые поддерживают определение типа MIME файла по имени файла или по содержимому файла (магические байты). Вот почему метод probeContentType () выдает исключение IOException, если реализация этого API использует предоставленный ему путь, чтобы фактически попытаться открыть связанный с ним файл.
Опять же, ванильный выполнение этого (тот, что идет с JDK) оставляет желать лучшего.
В каком-то идеальном мире в далекой-далекой галактике все эти библиотеки, которые пытаются решить эту проблему типа file-to-mime, просто реализуют java.nio.file.spi.FileTypeDetector, вы должны добавить jar-файл предпочтительной реализуемой библиотеки в свой путь к классам, и это будет Это.
В реальном мире, где вам нужен раздел TL, DR, вы должны найти библиотеку с наибольшим количеством звезд рядом с ее названием и использовать ее. В данном конкретном случае он мне не нужен (пока;)).
Вы можете сделать это с помощью одной строки: MimetypesFileTypeMap (). GetContentType (новый файл ("filename.ext")). Посмотрите полный тестовый код (Java 7):
import java.io.File;
import javax.activation.MimetypesFileTypeMap;
public class MimeTest {
public static void main(String a[]){
System.out.println(new MimetypesFileTypeMap().getContentType(
new File("/path/filename.txt")));
}
}
Этот код дает следующий результат: текст / простой
File file = new File(PropertiesReader.FILE_PATH);
MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
String mimeType = fileTypeMap.getContentType(file);
URLConnection uconnection = file.toURL().openConnection();
mimeType = uconnection.getContentType();
Хотя этот код может решить вопрос, включая объяснение действительно помогает улучшить качество вашего сообщения.
Я сделал это с помощью следующего кода.
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class MimeFileType {
public static void main(String args[]){
try{
URL url = new URL ("https://www.url.com.pdf");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
InputStream content = (InputStream)connection.getInputStream();
connection.getHeaderField("Content-Type");
System.out.println("Content-Type "+ connection.getHeaderField("Content-Type"));
BufferedReader in = new BufferedReader (new InputStreamReader(content));
}catch (Exception e){
}
}
}
Если вы работаете с сервлетом и вам доступен контекст сервлета, вы можете использовать:
getServletContext().getMimeType( fileName );
Что такое getServletContext?
Мне не удалось найти ничего для проверки типа MIME video/mp4, поэтому я сделал собственное решение.
Я случайно заметил, что Википедия ошибалась и что подпись файла 00 00 00 18 66 74 79 70 69 73 6F 6D неверна. четвертый байт (18) и все 70 (исключенные) после значительных изменений среди других допустимых файлов mp4.
Этот код по сути является копией / вставкой кода URLConnection.guessContentTypeFromStream, но адаптирован для video/mp4.
BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(content));
String mimeType = URLConnection.guessContentTypeFromStream(bis);
// Goes full barbaric and processes the bytes manually
if (mimeType == null){
// These ints converted in hex ar:
// 00 00 00 18 66 74 79 70 69 73 6F 6D
// which are the file signature (magic bytes) for .mp4 files
// from https://www.wikiwand.com/en/List_of_file_signatures
// just ctrl+f "mp4"
int[] mp4_sig = {0, 0, 0, 24, 102, 116, 121, 112};
bis.reset();
bis.mark(16);
int[] firstBytes = new int[8];
for (int i = 0; i < 8; i++) {
firstBytes[i] = bis.read();
}
// This byte doesn't matter for the file signature and changes
mp4_sig[3] = content[3];
bis.reset();
if (Arrays.equals(firstBytes, mp4_sig)){
mimeType = "video/mp4";
}
}
Успешно протестирован с 10 различными файлами .mp4.
Обновлено: Вот полезная ссылка (если она все еще в сети), где вы можете найти образцы многих типов. У меня нет этих видео, и я не знаю, у кого они есть, но они полезны для тестирования приведенного выше кода.
Простой и лучший вариант - получить тип MIME содержимого из местоположения файла.
Используйте этот импорт
import java.nio.file.Files;
import java.nio.file.Path;
Код
String type = Files.probeContentType(Path.of(imagePath));
Потому что так много ответов, связанных с библиотеками или непереносимым кодом; Я подумал, что поделюсь альтернативным способом, просто проверив магические байты потока или файла, тип которого вы хотите узнать, как я показал здесь: https://stackoverflow.com/a/65667558/3225638
Он использует родную java, но требует, чтобы вы заранее определили в перечислении типы, которые вы хотите обрабатывать / обнаруживать, но вам нужно будет сделать это только один раз.
Хороший обзор доступных библиотек можно найти на rgagnon.com/javadetails/java-0487.html.