Получение типа MIME файла в Java

336

Мне было просто интересно, как большинство людей выбирают mime-тип из файла на Java? До сих пор я попробовал два утилиты: JMimeMagic& Mime-Util.

Первый дал мне исключения памяти, второй не закрывает свои потоки должным образом. Мне просто интересно, есть ли у кого-то еще метод / библиотека, которые они использовали и работали правильно?

Ли Теобальд
источник
4
Хороший обзор доступных библиотек дан по адресу rgagnon.com/javadetails/java-0487.html
koppor
Я использовал класс, который был опубликован в качестве ответа здесь: stackoverflow.com/a/10140531/293280
Джошуа Пинтер
3
Тика должна быть ответом сейчас. Другие ответы ниже проливают свет на многие зависимости с Tika, но я не вижу ни одного с Tika-Core.
javamonkey79
@ javamonkey79, когда мы используем TIka, он закрывает файл и больше не может использоваться. Строка contentType = tika.detect (is).
Прохладный

Ответы:

326

В Java 7 теперь вы можете просто использовать Files.probeContentType(path).

Крис Моуфорт
источник
62
Имейте в виду, что Files.probeContentType (Path) содержит ошибки в нескольких ОС, и было отправлено много сообщений об ошибках. У меня была проблема с программным обеспечением, работающим на Ubuntu, но не работающим на Windows. Казалось, что на windows Files.probeContentType (Path) всегда возвращал null. Это была не моя система, поэтому я не проверял версию JRE или windows. Это были Windows 7 или 8, вероятно, с оракулом JRE для Java 7.
Серебро
13
Я бег на OS X 10.9 и я nullза .xml, .pngи .xhtmlфайлы. Я не знаю, делаю ли я что-то ужасно неправильно, но это кажется довольно ужасным.
36
Основным ограничением этого является то, что файл должен существовать в файловой системе. Это не работает с потоком или байтовым массивом и т. Д.
Necreaux
3
этот метод не может возвращать тип MIME , когда я удалить расширение из exmaple name.For если имя test.mp4 я изменить его в «тест» и метод возвращает null.Also я расширение изменить фильм в PNG и т.д. возвращает тип пантомимы детские
Сархан
10
Это бесполезно, если файл имеет отсутствующее или неправильное расширение.
шмосел
215

К сожалению,

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 (отличную библиотеку, использующую как расширение файла, так и содержимое).

Джошуа Фокс
источник
8
Идеальное решение - мне очень помогло! Заключение FileInputStreamв BufferedInputStreamкритическую часть - иначе guessContentTypeFromStreamвозвращается null(переданный InputStreamэкземпляр должен поддерживать отметки)
Юрий Наконечный
11
У Howerver URLConnectionочень ограниченный набор типов контента, который он распознает. Например, он не может обнаружить application/pdf.
кпентчев
3
Это только оставляет его запертым, потому что вы не оставили себе способа закрыть его. Отключение URLConnection разблокирует его.
маркиз Лорн
1
и предположим, что не совпадают ни догадывающиеся, ни догадывающиеся, но недопустимые, например, mp4
Хартмут П.
3
guessContentTypeFromName()использует $JAVA_HOME/lib/content-types.propertiesфайл по умолчанию . Вы можете добавить свой собственный расширенный файл, изменив системное свойствоSystem.setProperty("content.types.user.table","/lib/path/to/your/property/file");
Rasika Perera
50

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);
Адам Хошек
источник
4
К сожалению, как javadoc для getContentType(File)состояний: Возвращает MIME-тип файлового объекта. Реализация в этом классе вызывает getContentType(f.getName()).
Матиас
3
И помните, что вы можете расширить эту функциональность с помощью файла META-INF / mime.types, чтобы он был идеальным, если вы вынуждены использовать Java 6. docs.oracle.com/javaee/5/api/javax/activation/…
Chexpir
8
Вы можете пропустить создание нового объектаMimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file)
akostadinov
Спасибо за Ваш ответ. Это успешно работает для меня.
Рададия Никунь
Но он по-прежнему возвращает тип контента только на основе имени файла. И это особенно опасно для файлов, загружаемых пользователями.
Сергей Пономарев
47

С Apache Tika вам нужно всего три строчки кода :

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-интерфейсы богаты, он может анализировать «что угодно». Начиная с версии 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)

Смотрите apidocs для получения дополнительной информации.

lifeisfoo
источник
1
Это не работает для CSV. WTF? stackoverflow.com/questions/46960231/…
gstackoverflow
1
Одна плохая вещь о Тике, много раздувания зависимости. Это увеличило размер моей банки на 54 МБ !!!
helmy
1
@helmyTika 1.17 является автономной и имеет размер всего 648 КБ.
Сайнан
... или только new Tika().detect(file.toPath())для обнаружения на основе расширения файла, а не для обнаружения на основе содержимого файла
Lu55
@ Lu55 документы говорят, что по-прежнему использует содержимое документа. Я думаю, что вы имеете в виду new Tika().detect(file.getPath()), который использует только расширение файла
delucasvb
31

Apache Tika предлагает в tika-core обнаружение типа 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 (...) нельзя использовать напрямую ( TIKA-1120 ). Дополнительные советы предоставляются по адресу https://tika.apache.org/0.10/detection.html .

koppor
источник
1
+1 Также Metadata.RESOURCE_NAME_KEYможет быть опущен (если у вас его нет или вы не можете полагаться на оригинальное имя), но в этом случае вы получите неправильный результат в некоторых случаях (например, офисные документы).
user1516873
У него есть некоторые проблемы с обнаружением XLSX, если в имени файла нет расширения ... но это решение простое и элегантное.
Оскар Перес
23

Если вы разработчик Android, вы можете использовать служебный класс, android.webkit.MimeTypeMapкоторый отображает MIME-типы на расширения файлов и наоборот.

Следующий фрагмент кода может помочь вам.

private static String getMimeType(String fileUrl) {
    String extension = MimeTypeMap.getFileExtensionFromUrl(fileUrl);
    return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
Паван
источник
3
Это также работает, если использовать локальные пути к файлам, такие как /sdcard/path/to/video.extension. Проблема в том, что если локальный файл содержит место на своем пути, он всегда возвращает нуль
nmxprime
17

Из розеиндии :

FileNameMap fileNameMap = URLConnection.getFileNameMap();
String mimeType = fileNameMap.getContentTypeFor("alert.gif");
AlikElzin-kilaka
источник
7
Кто бы ни проголосовал против, пожалуйста, добавьте комментарий, чтобы я (и другие) могли научиться публиковать лучшие ответы.
АликЭльзин-килака
3
Я не голосовал против вас, но getFileNameMap не работает для многих основных типов файлов, например, «bmp». Также URLConnection.guessContentTypeFromName возвращает то же самое
Овидиу Булиган
5
Очень неполная функция. Начиная с Java 7, расширения html, pdf и jpeg возвращают правильный тип mime, но js и css возвращают ноль!
djsumdog
Я протестировал с 'webm', и он вернул ноль.
Энрике Роча
16

Если вы застряли с Java 5-6, то этот служебный класс из серво продукта с открытым исходным кодом .

Вам нужна только эта функция

public static String getContentType(byte[] data, String name)

Он проверяет первые байты содержимого и возвращает типы содержимого на основе этого содержимого, а не по расширению файла.

Овидиу Булиган
источник
Работал для простого, популярного и нескольких типов файлов, которые мне были нужны :)
user489041
13

Мне было просто интересно, как большинство людей выбирают mime-тип из файла на Java?

Я опубликовал свой пакет Java SimpleMagic , который позволяет определять тип содержимого (mime-type) из файлов и байтовых массивов. Он предназначен для чтения и запуска магических файлов команд Unix file (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();
}
Серый
источник
1
Протестировано на нескольких файлах изображений. У всех было расширение переименовано. Ваша удивительная библиотека справилась с этим правильно. Конечно, его свет тоже :).
saurabheights
1
Да, это хорошо работает. А для тех, кому нужно использовать это решение в Android, вы можете просто включить в файл build.gradle следующее: compile ('com.j256.simplemagic: simplemagic: 1.10')
jkincali
1
Это отличное решение! Спасибо!
javydreamercsw
5

Чтобы скинуть мои 5 центов:

TL, DR

Я использую MimetypesFileTypeMap и добавляю в файл mime.types любой mime, которого там нет, и он мне особенно нужен.

А теперь долго читал:

Прежде всего, список типов MIME огромен , см. Здесь: https://www.iana.org/assignments/media-types/media-types.xhtml

Мне нравится сначала использовать стандартные возможности, предоставляемые JDK, и если это не сработает, я пойду и поищу что-нибудь еще.

Определить тип файла по расширению файла

Начиная с 1.6, Java имеет MimetypesFileTypeMap, как указано в одном из ответов выше, и это самый простой способ определить тип mime:

new MimetypesFileTypeMap().getContentType( fileName );

В его ванильной реализации это мало что делает (т.е. работает для .html, но не для .png). Однако очень просто добавить любой тип контента, который вам может понадобиться:

  1. Создайте файл с именем «mime.types» в папке META-INF в вашем проекте
  2. Добавьте строку для каждого типа MIME, который вам нужен, и реализация по умолчанию не предоставляет (существуют сотни типов MIME, и список увеличивается с течением времени).

Пример записи для файлов 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 и сделать это в своем коде:

Files.probeContentType(Paths.get("either file name or full path goes here"));

Определение API предоставляет средства, которые поддерживают либо определение типа mime файла по имени файла, либо по содержимому файла (волшебные байты). Вот почему метод probeContentType () выбрасывает IOException, если реализация этого API использует предоставленный ему Path, чтобы фактически попытаться открыть связанный с ним файл.

Опять же, ванильная реализация этого (того, что идет с JDK) оставляет желать лучшего.

В каком-то идеальном мире в далекой-далекой галактике все эти библиотеки, которые пытаются решить эту проблему типа «файл-пантомима», просто реализуют java.nio.file.spi.FileTypeDetector , вы можете оставить jar- файл предпочтительной библиотеки реализации. файл в ваш путь к классу, и это было бы так.

В реальном мире, где вам нужен раздел TL, DR, вы должны найти библиотеку с большинством звездочек рядом с ее именем и использовать ее. Для этого конкретного случая мне не нужен (пока;)).

nidalpres
источник
3

Я попробовал несколько способов сделать это, включая первый, который сказал @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();
ricardoc
источник
5
У меня не было никакого успеха с MimeUtil2 - почти все возвращалось как application / octet-stream. Я использовал MimeUtil.getMimeTypes () с гораздо большим успехом после инициализации с помощью `MimeUtil.registerMimeDetector (" eu.medsea.mimeutil.detector.MagicMimeMimeDetector "); MimeUtil.registerMimeDetector ( "eu.medsea.mimeutil.detector.ExtensionMimeDetector"); MimeUtil.registerMimeDetector ( "eu.medsea.mimeutil.detector.OpendesktopMimeDetector"); `
Брайан Пипа
2
Спасибо за рабочее решение. В документации mime-util не очень ясно, как создать экземпляр класса утилит. Наконец, он был запущен и запущен, но заменил строку имени класса реальным классом. MimeUtil.registerMimeDetector (ExtensionMimeDetector.class.getName ()); String mimeType = MimeUtil.getMostSpecificMimeType (MimeUtil.getMimeTypes (filename)). ToString ();
Роб Юурлинк
2

Для загрузки файлов лучше использовать двухуровневую проверку.

Сначала вы можете проверить mimeType и проверить его.

Во-вторых, вы должны преобразовать первые 4 байта вашего файла в шестнадцатеричное, а затем сравнить его с магическими числами. Тогда это будет действительно безопасный способ проверки файлов.

javacreed
источник
2

Это самый простой способ, который я нашел для этого:

byte[] byteArray = ...
InputStream is = new BufferedInputStream(new ByteArrayInputStream(byteArray));
String mimeType = URLConnection.guessContentTypeFromStream(is);
madx
источник
Отличное решение!
Шерзод
2

Если вы работаете с сервлетом и если вам доступен контекст сервлета, вы можете использовать:

getServletContext().getMimeType( fileName );
Рамишка Дасанаяка
источник
1
Что такое getServletContext?
e-info128
1

весной файл MultipartFile ;

org.springframework.web.multipart.MultipartFile

file.getContentType();

Ахмед Р. Наземи
источник
0

если вы работаете на ОС 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'
Абденнур ТУМИ
источник
2
Это будет работать, но IMO - плохая практика, поскольку она связывает ваш код с конкретной ОС и требует наличия внешней утилиты в системе, в которой она работает. Не пойми меня неправильно; это полностью правильное решение, но оно нарушает мобильность - что является одной из основных причин использования Java в первую очередь ...
ToVine
@ToVine: для записи, я собираюсь уважительно не согласиться. Не каждая Java-программа должна быть переносимой. Пусть контекст и программист примут это решение. en.wikipedia.org/wiki/Java_Native_Interface
Заннон,
0

Попробовав другие библиотеки, я остановился на 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);
К. Шива Прасад Редди
источник
0
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) доступен начиная с версии 1.7 JDK, и он работает очень хорошо для меня.
Реза Рахими
Спасибо, только я не могу понять, почему некоторые пользователи проголосовали)))
Вазген Торосян
Вовсе нет, может быть, у них есть более ранняя версия JDK :)))
Реза Рахими
0

Вы можете сделать это только одной строкой: MimetypesFileTypeMap (). GetContentType (new File ("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")));
    }
}

Этот код производит следующий вывод: text / plain

Кассио Сеффрин
источник
0
File file = new File(PropertiesReader.FILE_PATH);
MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
String mimeType = fileTypeMap.getContentType(file);
URLConnection uconnection = file.toURL().openConnection();
mimeType = uconnection.getContentType();
Ганеш Вечалапу
источник
4
Хотя этот код может решить вопрос, в том числе объяснение действительно помогает улучшить качество вашего поста.
Shree
0

Я сделал это с помощью следующего кода.

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){

        }
    }
}
Сахмад
источник
0

Апач тика.

<!-- https://mvnrepository.com/artifact/org.apache.tika/tika-parsers -->
<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-parsers</artifactId>
    <version>1.24</version>
</dependency>

и две строки кода.

Tika tika=new Tika();
tika.detect(inputStream);

Снимок экрана ниже

введите описание изображения здесь

Пратик Гаурав
источник