Отримання типу Mime файлу в Java


336

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

Перший дав мені винятки з пам'яті, другий не закриває свої потоки належним чином. Мені було просто цікаво, чи хтось інший має метод / бібліотеку, якими вони користувались і працювали правильно?


4
Хороший огляд доступних бібліотек подано на rgagnon.com/javadetails/java-0487.html
koppor

Я використовував клас, який було розміщено як відповідь тут: stackoverflow.com/a/10140531/293280
Джошуа Пінтер

3
Тіка повинна відповісти зараз. Інші відповіді нижче засвідчують багато залежностей від Тіки, але я не бачу жодної з тика-ядром.
javamonkey79

@ javamonkey79, коли ми використовуємо TIka, він охоплює файл і його більше не можна використовувати. String contentType = tika.detect (є).
Класні Течі

Відповіді:


326

На Java 7 тепер можна просто використовувати Files.probeContentType(path).


62
Майте на увазі, що Files.probeContentType (Path) є помилкою на декількох ОС і було подано багато звітів про помилки. У мене виникли проблеми з програмним забезпеченням, що працює на ubuntu, але він не працював у Windows. Здавалося, що на Windows Files.probeContentType (Path) завжди повертається null. Це була не моя система, тому я не перевіряв версію JRE чи Windows. Це були вікна 7 або 8, ймовірно, з oracle JRE для java 7.
Срібло

13
Я біг на OS X 10.9 і я nullза .xml, .pngі .xhtmlфайли. Я не знаю, чи я роблю щось жахливо неправильне, але це здається досить жахливим.

36
Основним обмеженням цього є те, що файл повинен існувати у файловій системі. Це не працює із потоком чи байтовим масивом тощо.
Necreaux

3
цей метод не може повернути тип mime, коли я видаляю розширення з name.For exmaple, якщо ім'я test.mp4, я змінюю його на "test", а метод повертає null.Aso також я змінюю розширення фільму на png і т.д. він повертає png mime type
Sarkhan

10
Це марно, якщо у файлі відсутнє або неправильне розширення.
shmosel

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
Ховервер, URLConnectionмає дуже обмежений набір типів вмісту, який він розпізнає. Наприклад, він не в змозі виявити application/pdf.
кпенчев

3
Це залишає лише замкненим, тому що ви не можете залишити його. Відключення URLConnection розблокує його.
Маркіз Лорнський

1
обидві здогадкиContentTypeFromStream ні гаджеContentTypeFromName НЕ впізнають, наприклад mp4
Hartmut P.

3
guessContentTypeFromName()використовує $JAVA_HOME/lib/content-types.propertiesфайл за замовчуванням . ви можете додати свій розширений файл, змінивши властивість системиSystem.setProperty("content.types.user.table","/lib/path/to/your/property/file");
Rasika Perera

50

API JAF є частиною 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
На жаль, як getContentType(File)заявляє javadoc для : Повертає тип 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 багаті, він може проаналізувати "що завгодно". Станом на 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)

Докладнішу інформацію див. У apidocs .


1
Це не працює для csv. wtf? stackoverflow.com/questions/46960231 / ...
gstackoverflow

1
Одна погана річ про Тіку, велика кількість залежності. Це збільшило розмір моєї банки на 54 Мб !!!
гельмі

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не отримує інших залежностей, що робить його такою ж легкою, як і утиліта, яка не підтримується в даний час .

Простий приклад коду (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 .


1
+1 Також Metadata.RESOURCE_NAME_KEYможна пропустити (якщо у вас немає жодної або не можете покластися на оригінальне ім’я), але в такому випадку ви отримаєте неправильний результат у деяких випадках (наприклад, офісні документи).
користувач1516873

Є проблеми з виявленням 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". Проблема полягає в тому, що якщо локальний файл містить пробіл на своєму шляху, він завжди повертає null
nmxprime

17

Від roseindia :

FileNameMap fileNameMap = URLConnection.getFileNameMap();
String mimeType = fileNameMap.getContentTypeFor("alert.gif");

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?

Я опублікував мій пакет SimpleMagic Java, який дозволяє визначати тип вмісту (mime-type) з файлів та байтових масивів. Він призначений для читання та запуску магічних файлів командного файлу 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();
}

1
Тестували його на кількох файлах зображень. Усі були перейменовані на розширення. Ваша дивовижна бібліотека обробляла її належним чином. Звичайно, його світло теж :).
saurabheights

1
Так, це добре працює. А для тих, хто потребує використання цього рішення в Android, ви можете просто включити у файл build.gradle таке: compile ('com.j256.simplemagic: simplemagic: 1.10')
jkincali

1
Це чудове рішення! Дякую!
javydreamercsw

5

Щоб підключитися до моїх 5 центів:

TL, DR

Я використовую MimetypesFileTypeMap і додаю будь-яку міму, якої немає, і мені спеціально це потрібно, у файл 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). Однак додати будь-який тип вмісту, який вам може знадобитися, дуже просто:

  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.probeContentType(Paths.get("either file name or full path goes here"));

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

Знову ж таки, ванільна реалізація цього (тієї, що постачається з JDK) залишає бажати кращого.

У якомусь ідеальному світі в галактиці далеко-далеко, всі ці бібліотеки, які намагаються вирішити цю проблему типу файлу до міму, просто реалізують java.nio.file.spi.FileTypeDetector , ви в бажаний баночку бібліотеки, що реалізується. файл у ваш класний шлях і це було б все.

У реальному світі, тому, де вам потрібен розділ TL, DR, ви повинні знайти бібліотеку з більшою кількістю зірок поруч із своїм ім'ям та використовувати її. У цьому конкретному випадку мені це не потрібно (поки;)).


3

Я спробував кілька способів зробити це, включаючи перші, які сказав @Joshua Fox. Але деякі не розпізнають часті міметипи, як для файлів 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();

5
У мене зовсім не було успіху з MimeUtil2 - майже все повернулося як додаток / октет-потік. Я використовував 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 (назва файлу)). ToString ();
Роб Юрлінк

2

Краще використовувати дворівневу перевірку для завантаження файлів.

Спочатку ви можете перевірити mimeType і перевірити його.

По-друге, ви повинні подивитися, щоб перетворити перші 4 байти вашого файлу в шістнадцятковий, а потім порівняти його з магічними числами. Тоді це буде дійсно безпечний спосіб перевірити перевірку файлів.


2

Це найпростіший спосіб, який я знайшов для цього:

byte[] byteArray = ...
InputStream is = new BufferedInputStream(new ByteArrayInputStream(byteArray));
String mimeType = URLConnection.guessContentTypeFromStream(is);

Дуже найкраще рішення!
Шерзод

2

Якщо ви працюєте з сервлетом і якщо контекст сервлета доступний для вас, ви можете використовувати:

getServletContext().getMimeType( fileName );

1
Що таке getServletContext?
e-info128


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) доступний з JDK версії 1.7, і він працює дуже добре для мене.
Реза Рахімі

Дякую, тільки я не можу зрозуміти, чому деякі користувачі проголосували)))
Вазген Торосян

Зовсім не, можливо, у них є більш рання версія JDK :)))
Реза Рахімі

0

Ви можете зробити це лише одним рядком: MimetypesFileTypeMap (). GetContentType (новий файл ("ім'я файлу.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")));
    }
}

Цей код дає наступний вихід: текст / звичайний


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
Хоча цей код може вирішити питання, у тому числі пояснення дійсно допомагає покращити якість вашої публікації.
Шрі

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

Apache Tika.

<!-- 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);

Знімок екрана нижче

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.