Obtenir le type MIME d'un fichier en Java

336

Je me demandais simplement comment la plupart des gens récupèrent un type MIME à partir d'un fichier en Java? Jusqu'à présent, j'ai essayé deux utilitaires: JMimeMagic& Mime-Util.

Le premier m'a donné des exceptions de mémoire, le second ne ferme pas correctement ses flux. Je me demandais simplement si quelqu'un d'autre avait une méthode / bibliothèque qu'ils utilisaient et fonctionnaient correctement?

Lee Theobald
la source
4
Un bon aperçu des bibliothèques disponibles est donné sur rgagnon.com/javadetails/java-0487.html
koppor
J'ai utilisé la classe qui a été publiée comme réponse ici: stackoverflow.com/a/10140531/293280
Joshua Pinter
3
Tika devrait être la réponse maintenant. Les autres réponses ci-dessous font la lumière sur de nombreuses dépendances avec Tika, mais je n'en vois aucune avec tika-core.
javamonkey79
@ javamonkey79 lorsque nous utilisons TIka, il recouvre le fichier et il n'est plus utilisable. String contentType = tika.detect (est).
Cool Techie

Réponses:

326

Dans Java 7, vous pouvez maintenant simplement utiliser Files.probeContentType(path).

Chris Mowforth
la source
62
Sachez que Files.probeContentType (Path) est bogué sur plusieurs systèmes d'exploitation et de nombreux rapports de bogues ont été déposés. J'ai eu un problème avec un logiciel fonctionnant sur Ubuntu mais échouant sur Windows. Il semblait que sur windows Files.probeContentType (Path) renvoyait toujours null. Ce n'était pas mon système, donc je n'ai pas vérifié la version JRE ou Windows. C'était Windows 7 ou 8 probablement avec Oracle JRE pour Java 7.
Silver
13
Je suis en cours d' exécution sur OS X 10.9 et je reçois nullpour .xml, .pnget les .xhtmlfichiers. Je ne sais pas si je fais juste quelque chose d'horriblement mal, mais cela semble plutôt terrible.
36
Une limitation majeure à cela est que le fichier doit exister sur le système de fichiers. Cela ne fonctionne pas avec un flux ou un tableau d'octets, etc.
Necreaux
3
cette méthode ne peut pas retourner le type mime lorsque je supprime l'extension du nom.Pour exmaple si le nom est test.mp4 je le change en "test" et la méthode retourne null.Aussi je change l'extension du film en png, etc. elle renvoie le type mime png
Sarkhan
10
Cela est inutile si le fichier a une extension manquante ou incorrecte.
shmosel
215

Malheureusement,

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

ne fonctionne pas, car cette utilisation de l'URL laisse un fichier verrouillé, de sorte qu'il est, par exemple, non supprimable.

Cependant, vous avez ceci:

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

et également les suivants, qui ont l'avantage d'aller au-delà de la simple utilisation d'une extension de fichier, et jettent un œil au contenu

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

Cependant, comme suggéré par le commentaire ci-dessus, le tableau intégré des types MIME est assez limité, ne comprenant pas, par exemple, MSWord et PDF. Donc, si vous voulez généraliser, vous devrez aller au-delà des bibliothèques intégrées, en utilisant, par exemple, Mime-Util (qui est une excellente bibliothèque, en utilisant à la fois l'extension de fichier et le contenu).

Joshua Fox
la source
8
Solution parfaite - m'a beaucoup aidé! Enroulant FileInputStreamdans BufferedInputStreamfait partie essentielle - sinon le guessContentTypeFromStreamrendement null(passé InputStreaminstance doit soutenir les marques)
Yuriy Nakonechnyy
11
Cependant, il URLConnectiona un ensemble très limité de types de contenu qu'il reconnaît. Par exemple, il n'est pas en mesure de détecter application/pdf.
kpentchev
3
Il ne le laisse verrouillé que parce que vous ne vous êtes laissé aucun moyen de le fermer. Déconnecter l'URLConnection le déverrouillerait.
Marquis de Lorne
1
les deux guessContentTypeFromStream et guessContentTypeFromName ne reconnaissent PAS par exemple mp4
Hartmut P.
3
guessContentTypeFromName()utilise le $JAVA_HOME/lib/content-types.propertiesfichier par défaut . vous pouvez ajouter votre propre fichier étendu en changeant la propriété du systèmeSystem.setProperty("content.types.user.table","/lib/path/to/your/property/file");
Rasika Perera
50

L'API JAF fait partie de JDK 6. Regardez le javax.activationpackage.

Les classes les plus intéressantes sont javax.activation.MimeType- un détenteur de type MIME réel - et javax.activation.MimetypesFileTypeMap- une classe dont l'instance peut résoudre le type MIME en tant que chaîne pour un fichier:

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);
Adam Hošek
la source
4
Malheureusement, comme le javadoc pour les getContentType(File)états: Renvoie le type MIME de l'objet fichier. L'implémentation dans cette classe appelle getContentType(f.getName()).
Matyas
3
Et n'oubliez pas que vous pouvez étendre cette fonctionnalité avec le fichier META-INF / mime.types afin qu'elle soit parfaite si vous êtes obligé d'utiliser Java 6. docs.oracle.com/javaee/5/api/javax/activation/…
Chexpir
8
vous pouvez ignorer la création d'un nouvel objet parMimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file)
akostadinov
Merci pour votre réponse. Cela fonctionne avec succès pour moi.
Radadiya Nikunj
Mais il renvoie toujours le type de contenu uniquement en fonction du nom de fichier. Et cela est particulièrement dangereux pour les fichiers téléchargés par les utilisateurs.
Sergey Ponomarev
47

Avec Apache Tika, vous n'avez besoin que de trois lignes de code :

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

Si vous avez une console groovy, collez et exécutez ce code pour jouer avec:

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

Gardez à l'esprit que ses API sont riches, il peut analyser "n'importe quoi". Depuis tika-core 1.14, vous avez:

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)

Voir les apidocs pour plus d'informations.

lifeisfoo
la source
1
Cela ne fonctionne pas pour csv. wtf? stackoverflow.com/questions/46960231/…
gstackoverflow
1
Une mauvaise chose à propos de Tika, beaucoup de ballonnements de dépendance. Il a augmenté la taille de mon pot de 54 Mo !!!
helmy
1
@helmyTika 1.17 est autonome et ne fait que 648 Ko.
Sainan
... ou simplement new Tika().detect(file.toPath())pour la détection basée sur l'extension du fichier plutôt que la détection basée sur le contenu du fichier
Lu55
Les documents @ Lu55 indiquent que le contenu du document est toujours utilisé. Je pense que vous voulez dire new Tika().detect(file.getPath()), qui utilise uniquement l'extension de fichier
delucasvb
31

Apache Tika propose dans tika-core une détection de type mime basée sur des marqueurs magiques dans le préfixe de flux. tika-corene récupère pas les autres dépendances, ce qui le rend aussi léger que l' utilitaire de détection de type MIME actuellement non entretenu .

Exemple de code simple (Java 7), utilisant les variables theInputStreamettheFileName

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

Veuillez noter que MediaType.detect (...) ne peut pas être utilisé directement ( TIKA-1120 ). Plus d'indications sont fournies à https://tika.apache.org/0.10/detection.html .

koppor
la source
1
+1 Metadata.RESOURCE_NAME_KEYPeut également être omis (si vous n'en avez pas ou ne pouvez pas vous fier au nom d'origine), mais dans ce cas, vous obtiendrez un résultat erroné dans certains cas (documents de bureau par exemple).
user1516873
Il a quelques problèmes pour détecter XLSX s'il n'y a pas d'extension sur le nom de fichier ... mais cette solution est simple et élégante.
Oscar Pérez
23

Si vous êtes un développeur Android, vous pouvez utiliser une classe utilitaire android.webkit.MimeTypeMapqui mappe les types MIME aux extensions de fichier et vice versa.

L'extrait de code suivant peut vous aider.

private static String getMimeType(String fileUrl) {
    String extension = MimeTypeMap.getFileExtensionFromUrl(fileUrl);
    return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
Pawan
la source
3
Cela fonctionne également si vous essayez avec des chemins de fichiers locaux tels que "/sdcard/path/to/video.extension". Le problème est que si le fichier local contient de l'espace sur son chemin, il renvoie toujours null
nmxprime
17

De roseindia :

FileNameMap fileNameMap = URLConnection.getFileNameMap();
String mimeType = fileNameMap.getContentTypeFor("alert.gif");
AlikElzin-kilaka
la source
7
Quiconque a voté contre la réponse, veuillez ajouter un commentaire afin que moi (et d'autres) apprenions à publier de meilleures réponses.
AlikElzin-kilaka
3
Je ne vous ai pas rejeté, mais getFileNameMap ne fonctionne pas pour de nombreux types de fichiers de base, par exemple «bmp». URLConnection.guessContentTypeFromName renvoie également la même chose
Ovidiu Buligan
5
Fonction très incomplète. Depuis Java 7, les extensions html, pdf et jpeg renvoient le type mime correct mais js et css renvoient null!
djsumdog
J'ai testé avec 'webm' et il est retourné nul.
Henrique Rocha
16

Si vous êtes bloqué avec java 5-6, alors cette classe d'utilité de produit open source servoy .

Vous avez seulement besoin de cette fonction

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

Il sonde les premiers octets du contenu et renvoie les types de contenu en fonction de ce contenu et non par extension de fichier.

Ovidiu Buligan
la source
A travaillé pour les types de fichiers simples, populaires et rares dont j'avais besoin :)
user489041
13

Je me demandais simplement comment la plupart des gens récupéraient un type MIME à partir d'un fichier en Java?

J'ai publié mon package Java SimpleMagic qui permet la détermination du type de contenu (type mime) à partir de fichiers et de tableaux d'octets. Il est conçu pour lire et exécuter les fichiers magiques de commande de fichier Unix (1) qui font partie de la plupart des configurations de système d'exploitation Unix.

J'ai essayé Apache Tika, mais il est énorme avec des tonnes de dépendances, URLConnectionn'utilise pas les octets des fichiers et MimetypesFileTypeMapne regarde que les noms de fichiers.

Avec SimpleMagic, vous pouvez faire quelque chose comme:

// 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();
}
gris
la source
1
Testé sur plusieurs fichiers image. Tous avaient une extension renommée. Votre bibliothèque impressionnante l'a gérée correctement. Bien sûr, sa lumière aussi :).
saurabheights
1
Oui, cela fonctionne bien. Et pour ceux qui ont besoin d'utiliser cette solution dans Android, vous pouvez simplement inclure les éléments suivants dans le fichier build.gradle: compile ('com.j256.simplemagic: simplemagic: 1.10')
jkincali
1
C'est une excellente solution! Merci!
javydreamercsw
5

Pour participer avec mes 5 cents:

TL, DR

J'utilise MimetypesFileTypeMap et j'ajoute tout mime qui n'est pas là et j'en ai spécifiquement besoin, dans le fichier mime.types.

Et maintenant, la longue lecture:

Tout d'abord, la liste des types MIME est énorme , voir ici: https://www.iana.org/assignments/media-types/media-types.xhtml

J'aime d'abord utiliser les installations standard fournies par JDK, et si cela ne fonctionne pas, je vais chercher autre chose.

Déterminer le type de fichier à partir de l'extension de fichier

Depuis 1.6, Java a MimetypesFileTypeMap, comme indiqué dans l'une des réponses ci-dessus, et c'est le moyen le plus simple de déterminer le type MIME:

new MimetypesFileTypeMap().getContentType( fileName );

Dans son implémentation vanilla, cela ne fait pas grand-chose (c'est-à-dire que cela fonctionne pour .html mais pas pour .png). Il est cependant très simple d'ajouter tout type de contenu dont vous pourriez avoir besoin:

  1. Créez un fichier nommé 'mime.types' dans le dossier META-INF de votre projet
  2. Ajoutez une ligne pour chaque type MIME dont vous avez besoin et l'implémentation par défaut ne fournit pas (il existe des centaines de types MIME et la liste s'allonge au fil du temps).

Des exemples d'entrées pour les fichiers png et js seraient:

image/png png PNG
application/javascript js

Pour le format de fichier mime.types, voir plus de détails ici: https://docs.oracle.com/javase/7/docs/api/javax/activation/MimetypesFileTypeMap.html

Déterminer le type de fichier à partir du contenu du fichier

Depuis la version 1.7, Java a java.nio.file.spi.FileTypeDetector , qui définit une API standard pour déterminer un type de fichier de manière spécifique à l' implémentation .

Pour récupérer le type MIME pour un fichier, vous devez simplement utiliser Fichiers et le faire dans votre code:

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

La définition de l'API fournit des fonctionnalités qui prennent en charge la détermination du type de mime de fichier à partir du nom de fichier ou du contenu du fichier (octets magiques). C'est pourquoi probeContentType () méthode lève IOException, dans le cas où une implémentation de cette API utilise le chemin d'accès qui lui est fourni pour réellement essayer d'ouvrir le fichier qui lui est associé.

Encore une fois, l' implémentation vanilla de celle-ci (celle qui vient avec JDK) laisse beaucoup à désirer.

Dans un monde idéal dans une galaxie lointaine, très lointaine, toutes ces bibliothèques qui tentent de résoudre ce problème de type fichier à mime implémenteraient simplement java.nio.file.spi.FileTypeDetector , vous déposeriez dans le pot de la bibliothèque d'implémentation préférée fichier dans votre chemin de classe et ce serait tout.

Dans le monde réel, celui où vous avez besoin de la section TL, DR, vous devriez trouver la bibliothèque avec la plupart des étoiles à côté de son nom et l'utiliser. Pour ce cas particulier, je n'en ai pas besoin (encore;)).

nidalpres
la source
3

J'ai essayé plusieurs façons de le faire, y compris les premières dites par @Joshua Fox. Mais certains ne reconnaissent pas les mimetypes fréquents comme pour les fichiers PDF, et d'autres ne peuvent pas être fiables avec de faux fichiers (j'ai essayé avec un fichier RAR avec une extension changée en TIF). La solution que j'ai trouvée, comme le dit également @Joshua Fox de manière superficielle, consiste à utiliser MimeUtil2 , comme ceci:

MimeUtil2 mimeUtil = new MimeUtil2();
mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
String mimeType = MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(file)).toString();
ricardoc
la source
5
Je n'ai eu aucun succès avec MimeUtil2 - presque tout est revenu sous forme de flux d'application / octet. J'ai utilisé MimeUtil.getMimeTypes () avec beaucoup plus de succès après l'initialisation avec `MimeUtil.registerMimeDetector (" eu.medsea.mimeutil.detector.MagicMimeMimeDetector "); MimeUtil.registerMimeDetector ("eu.medsea.mimeutil.detector.ExtensionMimeDetector"); MimeUtil.registerMimeDetector ("eu.medsea.mimeutil.detector.OpendesktopMimeDetector"); `
Brian Pipa
2
Merci pour la solution de travail. La documentation de mime-util n'est pas très claire sur la façon d'instancier la classe d'utilitaires. Enfin, il a été opérationnel, mais a remplacé la chaîne de nom de classe par la classe réelle. MimeUtil.registerMimeDetector (ExtensionMimeDetector.class.getName ()); String mimeType = MimeUtil.getMostSpecificMimeType (MimeUtil.getMimeTypes (nom de fichier)). ToString ();
Rob Juurlink
2

Il est préférable d'utiliser une validation à deux couches pour le téléchargement de fichiers.

Vous pouvez d'abord vérifier le mimeType et le valider.

Deuxièmement, vous devriez chercher à convertir les 4 premiers octets de votre fichier en hexadécimal, puis à le comparer avec les nombres magiques. Ce sera alors un moyen très sûr de vérifier les validations de fichiers.

javacré
la source
2

C'est la manière la plus simple que j'ai trouvée pour faire ceci:

byte[] byteArray = ...
InputStream is = new BufferedInputStream(new ByteArrayInputStream(byteArray));
String mimeType = URLConnection.guessContentTypeFromStream(is);
madx
la source
La meilleure solution!
Sherzod
2

Si vous travaillez avec un servlet et si le contexte du servlet est à votre disposition, vous pouvez utiliser:

getServletContext().getMimeType( fileName );
Ramishka Dasanayaka
la source
1
Qu'est-ce que c'est getServletContext?
e-info128
1

au printemps fichier MultipartFile ;

org.springframework.web.multipart.MultipartFile

file.getContentType();

Ahmad R. Nazemi
la source
0

si vous travaillez sur Linux OS, il y a une ligne de commande 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 "";
}

ensuite

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

mimetype("/var/www/ggg/au.mp3") //  'audio/mp3'
Abdennour TOUMI
la source
2
Cela fonctionnera, mais IMO est une mauvaise pratique car il lie votre code à un système d'exploitation spécifique et nécessite que l'utilitaire externe soit présent sur le système qui l'exécute. Ne vous méprenez pas; c'est une solution entièrement valide, mais qui rompt la portabilité - ce qui est l'une des principales raisons d'utiliser Java en premier lieu ...
ToVine
@ToVine: Pour mémoire, je vais respectueusement être en désaccord. Tous les programmes Java ne doivent pas nécessairement être portables. Laissez le contexte et le programmeur prendre cette décision. en.wikipedia.org/wiki/Java_Native_Interface
Zahnon
0

Après avoir essayé plusieurs autres bibliothèques, je me suis installé avec 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);
K. Siva Prasad Reddy
la source
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;
}
Vazgen Torosyan
la source
Cette méthode Files.probeContentType (String) est disponible depuis la version 1.7 du JDK et elle fonctionne très bien pour moi.
Reza Rahimi du
Merci, seulement je ne comprends pas pourquoi certains utilisateurs ont voté contre)))
Vazgen Torosyan
Pas du tout, peut-être qu'ils ont une version antérieure de JDK :)))
Reza Rahimi
0

Vous pouvez le faire avec une seule ligne: MimetypesFileTypeMap (). GetContentType (new File ("filename.ext")) . Regardez le code de test complet (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")));
    }
}

Ce code produit la sortie suivante: text / plain

Cassio Seffrin
la source
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();
ganesh vechalapu
la source
4
Bien que ce code puisse résoudre la question, y compris une explication aide vraiment à améliorer la qualité de votre message.
Shree
0

Je l'ai fait avec le code suivant.

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

        }
    }
}
sahmad
la source
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>

et Deux lignes de code.

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

Capture d'écran ci-dessous

entrez la description de l'image ici

Pratik Gaurav
la source