Uzyskiwanie pliku typu MIME w Javie


336

Zastanawiałem się, jak większość ludzi pobiera typ MIME z pliku w Javie? Do tej pory wypróbowałem dwa narzędzia: JMimeMagic& Mime-Util.

Pierwszy dał mi wyjątki pamięci, drugi nie zamyka poprawnie swoich strumieni. Zastanawiałem się tylko, czy ktoś miał metodę / bibliotekę, której używał i działał poprawnie?


4
Dobry przegląd dostępnych bibliotek znajduje się na stronie rgagnon.com/javadetails/java-0487.html
koppor

Użyłem klasy, która została opublikowana jako odpowiedź tutaj: stackoverflow.com/a/10140531/293280
Joshua Pinter

3
Tika powinna być teraz odpowiedzią. Pozostałe odpowiedzi poniżej wyjaśniają wiele zależności z Tika, ale nie widzę żadnej z tika-core.
javamonkey79

@ javamonkey79, gdy korzystamy z TIka, obejmuje on plik i nie jest już użyteczny. String contentType = tika.detect (is).
Cool Techie

Odpowiedzi:


326

W Javie 7 możesz teraz po prostu używać Files.probeContentType(path).


62
Należy pamiętać, że Files.probeContentType (ścieżka) jest wadliwy w wielu systemach operacyjnych i zostało zgłoszonych wiele raportów o błędach. Miałem problem z oprogramowaniem działającym na Ubuntu, ale nie działało na Windowsie. Wydawało się, że w systemie Windows pliki.probeContentType (ścieżka) zawsze zwracały wartość null. To nie był mój system, więc nie sprawdziłem wersji JRE ani Windows. To był Windows 7 lub 8, prawdopodobnie z JRE wyrocznią Jacle dla Javy 7.
Srebrny

13
Biegnę na OS X 10.9 i dostać nullsię do .xml, .pngi .xhtmlplików. Nie wiem, czy robię coś okropnie złego, ale wydaje się to dość okropne.

36
Głównym ograniczeniem jest to, że plik musi istnieć w systemie plików. To nie działa ze strumieniem lub tablicą bajtów itp.
Necreaux

3
ta metoda nie może zwrócić typu mime po usunięciu rozszerzenia z nazwy. Dla exmaple, jeśli nazwa to test.mp4 zmieniam go na „test” i metoda zwraca null. Zmieniam także rozszerzenie filmu na png itp. zwraca png typ mime
Sarkhan

10
Jest to bezużyteczne, jeśli plik ma brakujące lub nieprawidłowe rozszerzenie.
shmosel

215

Niestety,

mimeType = file.toURL().openConnection().getContentType();

nie działa, ponieważ takie użycie adresu URL pozostawia plik zablokowany, więc na przykład nie można go usunąć.

Masz jednak:

mimeType= URLConnection.guessContentTypeFromName(file.getName());

a także następujące, które mają tę zaletę, że wykraczają poza zwykłe korzystanie z rozszerzenia pliku i zerkają na treść

InputStream is = new BufferedInputStream(new FileInputStream(file));
mimeType = URLConnection.guessContentTypeFromStream(is);
 //...close stream

Jednak, jak sugeruje powyższy komentarz, wbudowana tabela typów mime jest dość ograniczona, nie obejmuje na przykład MSWord i PDF. Jeśli chcesz uogólnić, musisz wyjść poza wbudowane biblioteki, używając np. Mime-Util (która jest świetną biblioteką, wykorzystującą zarówno rozszerzenie pliku, jak i zawartość).


8
Idealne rozwiązanie - bardzo mi pomogło! Owijając FileInputStreamsię BufferedInputStreamjest kluczowym elementem - w przeciwnym razie guessContentTypeFromStreampowróci null(przeszedł InputStreaminstancja powinna wspierać znaków)
Jurij Nakonechnyy

11
Howerver, URLConnectionma bardzo ograniczony zestaw typów treści, które rozpoznaje. Na przykład nie jest w stanie wykryć application/pdf.
kpentchev

3
Pozostawia to tylko zamknięte, ponieważ nie pozostawiłeś sobie żadnego sposobu, aby je zamknąć. Odłączenie połączenia URL spowoduje jego odblokowanie.
Markiz Lorne

1
zarówno guessContentTypeFromStream, jak i guessContentTypeFromName NIE rozpoznają np. mp4
Hartmut P.

3
guessContentTypeFromName()używa $JAVA_HOME/lib/content-types.propertiespliku domyślnego . możesz dodać własny rozszerzony plik, zmieniając właściwość systemuSystem.setProperty("content.types.user.table","/lib/path/to/your/property/file");
Rasika Perera

50

Interfejs API JAF jest częścią JDK 6. Spójrz na javax.activationpakiet.

Najbardziej interesujące klasy to javax.activation.MimeType- faktyczny posiadacz typu MIME - i javax.activation.MimetypesFileTypeMap- klasa, której instancja może rozpoznać typ MIME jako ciąg znaków dla pliku:

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
Niestety, jako javadoc dla getContentType(File)stanów: Zwraca typ MIME obiektu pliku. Implementacja w tej klasie wywołuje getContentType(f.getName()).
Matyas

3
I pamiętaj, że możesz rozszerzyć tę funkcjonalność o plik META-INF / mime.types, więc jest idealny, jeśli jesteś zmuszony do korzystania z Java 6. docs.oracle.com/javaee/5/api/javax/activation/…
Chexpir

8
możesz pominąć tworzenie nowego obiektu przezMimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file)
akostadinov

Dzięki za odpowiedź. Z powodzeniem dla mnie działa.
Radadiya Nikunj

Ale nadal zwraca typ zawartości tylko na podstawie nazwy pliku. Jest to szczególnie niebezpieczne w przypadku plików przesyłanych przez użytkowników.
Sergey Ponomarev

47

Z Apache Tika potrzebujesz tylko trzech linii kodu :

File file = new File("/path/to/file");
Tika tika = new Tika();
System.out.println(tika.detect(file));

Jeśli masz świetną konsolę, po prostu wklej i uruchom ten kod, aby się nim bawić:

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

Należy pamiętać, że jego interfejsy API są bogate, może analizować „wszystko”. Od wersji 1.14 tika-core masz:

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)

Zobacz apidocs, aby uzyskać więcej informacji.



1
Jedna zła rzecz w Tice, dużo wzdęć uzależnienia. To zwiększyło rozmiar mojego słoika o 54 MB !!!
helmy

1
@helmyTika 1.17 jest samodzielny i ma tylko 648 KB.
Sainan

... lub po prostu new Tika().detect(file.toPath())wykrywanie oparte na rozszerzeniu pliku zamiast wykrywanie oparte na zawartości pliku
Lu55

@ Dokumenty Lu55 mówią, że nadal korzysta z treści dokumentu. Myślę, że masz na myśli new Tika().detect(file.getPath()), który używa tylko rozszerzenia pliku
delucasvb

31

Apache Tika oferuje w tika-core wykrywanie typu mime oparte na magicznych znacznikach w prefiksie strumienia. tika-corenie pobiera innych zależności, co czyni go tak lekkim, jak obecnie nieobsługiwane narzędzie do wykrywania typu Mime .

Prosty przykład kodu (Java 7), przy użyciu zmiennych theInputStreamitheFileName

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();
}

Należy pamiętać, że MediaType.detect (...) nie można używać bezpośrednio ( TIKA-1120 ). Więcej wskazówek można znaleźć na stronie https://tika.apache.org/0.10/detection.html .


1
+1 Metadata.RESOURCE_NAME_KEYMożna również pominąć (jeśli nie masz lub nie możesz polegać na oryginalnej nazwie), ale w takim przypadku w niektórych przypadkach otrzymasz błędny wynik (na przykład dokumenty biurowe).
user1516873 17.04.17

Ma pewne problemy z wykryciem XLSX, jeśli nie ma rozszerzenia nazwy pliku ... ale to rozwiązanie jest proste i eleganckie.
Oscar Pérez

23

Jeśli jesteś programistą Androida, możesz użyć klasy narzędzia, android.webkit.MimeTypeMapktóra mapuje typy MIME na rozszerzenia plików i odwrotnie.

Poniższy fragment kodu może ci pomóc.

private static String getMimeType(String fileUrl) {
    String extension = MimeTypeMap.getFileExtensionFromUrl(fileUrl);
    return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}

3
Działa to również w przypadku wypróbowania lokalnych ścieżek plików, takich jak „/sdcard/path/to/video.extension”. Problem polega na tym, że jeśli plik lokalny zawiera miejsce na ścieżce, zawsze zwraca wartość null
nmxprime

17

Z roseindii :

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

7
Ktokolwiek obniżył głos w odpowiedzi, dodaj komentarz, abyśmy (i inni) mogli nauczyć się publikować lepsze odpowiedzi.
AlikElzin-kilaka

3
Nie głosowałem za tobą, ale getFileNameMap nie działa dla wielu podstawowych typów plików, na przykład „bmp”. Zwraca również to samo URLConnection.guessContentTypeFromName
Ovidiu Buligan,

5
Bardzo niekompletna funkcja. Od wersji Java 7 rozszerzenia html, pdf i jpeg zwracają poprawny typ MIME, ale js i css zwracają wartość null!
djsumdog

Testowałem z „webm” i zwrócił wartość null.
Henrique Rocha,

16

Jeśli utkniesz z javą 5-6, to ta klasa narzędziowa z serwomechanizmu produktu open source .

Potrzebujesz tylko tej funkcji

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

Sprawdza pierwsze bajty zawartości i zwraca typy zawartości na podstawie tej zawartości, a nie według rozszerzenia pliku.


Pracowałem dla prostych, popularnych i kilku typów plików, których potrzebowałem :)
user489041

13

Zastanawiałem się, jak większość ludzi pobiera typ MIME z pliku w Javie?

Opublikowałem mój pakiet Java SimpleMagic , który umożliwia określanie typu zawartości (typu MIME) na podstawie plików i tablic bajtów. Jest przeznaczony do odczytu i uruchamiania magicznych plików poleceń Unix file (1), które są częścią większości ~ konfiguracji systemu operacyjnego Unix.

Próbowałem Apache Tika, ale jest ogromny z mnóstwem zależności, URLConnectionnie używa bajtów plików, a MimetypesFileTypeMaptakże tylko patrzy na nazwy plików.

Dzięki SimpleMagic możesz zrobić coś takiego:

// 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
Przetestowano na wielu plikach graficznych. Wszystkie zmieniono nazwy rozszerzeń. Twoja wspaniała biblioteka poradziła sobie z tym poprawnie. Oczywiście jego światło też :).
saurabheights,

1
Tak, to działa dobrze. A dla tych, którzy muszą korzystać z tego rozwiązania w systemie Android, możesz po prostu dołączyć do pliku build.gradle: compile ('com.j256.simplemagic: simplemagic: 1.10')
jkincali

1
To świetne rozwiązanie! Dzięki!
javydreamercsw

5

Aby włożyć moje 5 centów:

TL, DR

Korzystam z MimetypesFileTypeMap i dodam każdy mime, którego nie ma, a szczególnie go potrzebuję, do pliku mime.types.

A teraz długo czytam:

Przede wszystkim lista typów MIME jest ogromna , patrz tutaj: https://www.iana.org/assignments/media-types/media-types.xhtml

Najpierw lubię korzystać ze standardowych udogodnień dostarczonych przez JDK, a jeśli to nie zadziała, pójdę poszukać czegoś innego.

Określ typ pliku na podstawie rozszerzenia pliku

Od wersji 1.6 Java ma MimetypesFileTypeMap, jak wskazano w jednej z powyższych odpowiedzi, i jest to najprostszy sposób na określenie typu MIME:

new MimetypesFileTypeMap().getContentType( fileName );

W swojej waniliowej implementacji nie robi to wiele (tj. Działa na .html, ale nie działa na .png). Jednak bardzo łatwo jest dodać dowolny typ zawartości, który może być potrzebny:

  1. Utwórz plik o nazwie „mime.types” w folderze META-INF w swoim projekcie
  2. Dodaj wiersz dla każdego potrzebnego typu MIME, a domyślna implementacja nie zapewnia (istnieją setki typów MIME i lista rośnie w miarę upływu czasu).

Przykładowe wpisy dla plików png i js to:

image/png png PNG
application/javascript js

Aby uzyskać informacje na temat formatu pliku mime.types, zobacz więcej szczegółów tutaj: https://docs.oracle.com/javase/7/docs/api/javax/activation/MimetypesFileTypeMap.html

Określ typ pliku na podstawie zawartości pliku

Od wersji 1.7 Java ma java.nio.file.spi.FileTypeDetector , który definiuje standardowy interfejs API do określania typu pliku w sposób specyficzny dla implementacji .

Aby pobrać typ MIME dla pliku, wystarczy użyć Pliki i zrobić to w kodzie:

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

Definicja API zapewnia funkcje, które obsługują określanie typu MIME pliku na podstawie nazwy pliku lub zawartości pliku (bajty magiczne). Dlatego metoda probeContentType () zgłasza wyjątek IOException w przypadku, gdy implementacja tego interfejsu API używa podanej do niego ścieżki do faktycznego otwarcia pliku z nim powiązanego.

Ponownie, waniliowa implementacja tego (tego, który jest dostarczany z JDK) pozostawia wiele do życzenia.

W jakimś idealnym świecie w odległej galaktyce, wszystkie te biblioteki, które próbują rozwiązać ten problem typu „plik do mima”, po prostu zaimplementowałyby java.nio.file.spi.FileTypeDetector , upuściłbyś słoik preferowanej biblioteki implementującej plik do ścieżki klasy i to by było na tyle.

W prawdziwym świecie, w którym potrzebujesz sekcji TL, DR, powinieneś znaleźć bibliotekę z większością gwiazdek obok jej nazwy i użyć jej. W tym konkretnym przypadku nie potrzebuję (jeszcze;)).


3

Próbowałem tego dokonać na kilka sposobów, w tym pierwsze wypowiedziane przez @Joshua Fox. Ale niektórzy nie rozpoznają częstych typów mimetycznych, takich jak pliki PDF, a innym nie można ufać fałszywym plikom (próbowałem z plikiem RAR z rozszerzeniem zmienionym na TIF). Rozwiązaniem, które znalazłem, jak również powierzchownie powiedział @Joshua Fox, jest użycie MimeUtil2 w następujący sposób:

MimeUtil2 mimeUtil = new MimeUtil2();
mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
String mimeType = MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(file)).toString();

5
Z MimeUtil2 nie miałem żadnego sukcesu - prawie wszystko wróciło jako application / octet-stream. Użyłem MimeUtil.getMimeTypes () ze znacznie większym powodzeniem po zainicjowaniu za pomocą `MimeUtil.registerMimeDetector („ eu.medsea.mimeutil.detector.MagicMimeMimeDetector ”); MimeUtil.registerMimeDetector („eu.medsea.mimeutil.detector.ExtensionMimeDetector”); MimeUtil.registerMimeDetector („eu.medsea.mimeutil.detector.OpendesktopMimeDetector”); `
Brian Pipa

2
Dzięki za działające rozwiązanie. Dokumentacja mime-util nie jest bardzo jasna na temat tworzenia instancji klasy narzędzia. W końcu uruchomiłem go, ale zastąpiłem ciąg nazwy klasy rzeczywistą klasą. MimeUtil.registerMimeDetector (ExtensionMimeDetector.class.getName ()); Ciąg mimeType = MimeUtil.getMostSpecificMimeType (MimeUtil.getMimeTypes (nazwa pliku)). ToString ();
Rob Juurlink

2

Lepiej jest użyć weryfikacji dwuwarstwowej do przesyłania plików.

Najpierw możesz sprawdzić mimeType i zweryfikować go.

Po drugie, powinieneś przekonwertować pierwsze 4 bajty pliku na szesnastkowy, a następnie porównać go z liczbami magicznymi. Będzie to naprawdę bezpieczny sposób sprawdzania poprawności plików.


2

Oto najprostszy sposób na zrobienie tego:

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

Bardzo najlepsze rozwiązanie!
Sherzod

2

Jeśli pracujesz z serwletem i jeśli kontekst serwletu jest dostępny, możesz użyć:

getServletContext().getMimeType( fileName );

1
Co to jest getServletContext?
e-info128

1

wiosną plik MultipartFile ;

org.springframework.web.multipart.MultipartFile

file.getContentType();


0

jeśli pracujesz na systemie operacyjnym Linux, istnieje linia poleceń 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 "";
}

Następnie

mimetype("/home/nyapp.war") //  'application/zip'

mimetype("/var/www/ggg/au.mp3") //  'audio/mp3'

2
To zadziała, ale IMO jest złą praktyką, ponieważ wiąże kod z określonym systemem operacyjnym i wymaga obecności zewnętrznego narzędzia w systemie, w którym jest uruchomiony. Nie zrozum mnie źle; jest to w pełni poprawne rozwiązanie, ale psuje przenośność - co jest jednym z głównych powodów, dla których warto używać Javy ...
ToVine,

@ToVine: Dla przypomnienia, z szacunkiem się nie zgodzę. Nie każdy program Java musi być przenośny. Niech kontekst i programista podejmą tę decyzję. en.wikipedia.org/wiki/Java_Native_Interface
Zahnon

0

Po wypróbowaniu różnych innych bibliotek zdecydowałem się na 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;
}

Ta metoda Files.probeContentType (String) jest dostępna od wersji JDK 1.7 i działa dla mnie bardzo dobrze.
Reza Rahimi,

Dzięki, tylko nie rozumiem, dlaczego niektórzy użytkownicy
odmówili

Wcale nie, może mają wcześniejszą wersję JDK :)))
Reza Rahimi

0

Możesz to zrobić za pomocą tylko jednej linii: MimetypesFileTypeMap (). GetContentType (nowy plik („nazwa_pliku.ext”)) . Spójrz na pełny kod testowy (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")));
    }
}

Ten kod generuje następujące dane wyjściowe: tekst / zwykły


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
Chociaż ten kod może rozwiązać pytanie, wyjaśnienie naprawdę pomaga poprawić jakość posta.
Shree

0

Zrobiłem to z następującym kodem.

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>

i dwie linie kodu.

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

Zrzut ekranu poniżej

wprowadź opis zdjęcia tutaj

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.