Obtener el tipo Mime de un archivo en Java

336

Me preguntaba cómo la mayoría de las personas obtienen un tipo MIME de un archivo en Java. Hasta ahora he probado dos utilidades: JMimeMagic& Mime-Util.

El primero me dio excepciones de memoria, el segundo no cierra sus secuencias correctamente. Me preguntaba si alguien más tenía un método / biblioteca que usaban y funcionaban correctamente.

Lee Theobald
fuente
44
Se ofrece una buena descripción general de las bibliotecas disponibles en rgagnon.com/javadetails/java-0487.html
koppor
Usé la clase que fue publicada como respuesta aquí: stackoverflow.com/a/10140531/293280
Joshua Pinter
3
Tika debería ser la respuesta ahora. Las otras respuestas a continuación aclaran muchas dependencias con Tika, pero no veo ninguna con tika-core.
javamonkey79
@ javamonkey79 cuando usamos TIka, oculta el archivo y ya no se puede usar. Cadena contentType = tika.detect (es).
Cool Techie

Respuestas:

326

En Java 7 ahora solo puede usar Files.probeContentType(path).

Chris Mowforth
fuente
62
Tenga en cuenta que Files.probeContentType (Path) tiene errores en varios sistemas operativos y se han archivado muchos informes de errores. He tenido un problema con el software que funciona en ubuntu pero falla en windows. Parecía que en Windows Files.probeContentType (Path) siempre devuelve nulo. No era mi sistema, así que no verifiqué el JRE o la versión de Windows. Era Windows 7 u 8 probablemente con Oracle JRE para Java 7.
Plata
13
Estoy corriendo en OS X 10.9 y me sale nullhacia fuera para .xml, .pngy .xhtmlarchivos. No sé si solo estoy haciendo algo terriblemente mal, pero eso parece bastante terrible.
36
Una limitación importante con esto es que el archivo debe existir en el sistema de archivos. Esto no funciona con una secuencia o una matriz de bytes, etc.
Necreaux
3
este método no puede devolver el tipo mime cuando elimino la extensión del nombre. Por ejemplo, si el nombre es test.mp4, lo cambio a "test" y el método devuelve nulo. También cambio la extensión de la película a png, etc. devuelve el tipo mime png
Sarkhan
10
Esto es inútil si el archivo tiene una extensión faltante o incorrecta.
shmosel
215

Desafortunadamente,

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

no funciona, ya que este uso de URL deja un archivo bloqueado, por lo que, por ejemplo, no se puede borrar.

Sin embargo, tienes esto:

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

y también lo siguiente, que tiene la ventaja de ir más allá del mero uso de la extensión de archivo, y echa un vistazo al contenido

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

Sin embargo, como lo sugiere el comentario anterior, la tabla integrada de tipos MIME es bastante limitada, sin incluir, por ejemplo, MSWord y PDF. Por lo tanto, si desea generalizar, deberá ir más allá de las bibliotecas integradas, utilizando, por ejemplo, Mime-Util (que es una gran biblioteca, que utiliza tanto la extensión de archivo como el contenido).

Joshua Fox
fuente
8
Solución perfecta: ¡me ayudó mucho! Envolver FileInputStreamen BufferedInputStreames parte crucial - de lo contrario guessContentTypeFromStreamretorna null(pasado InputStreaminstancia debe apoyar marcas)
Yuriy Nakonechnyy
11
Howerver, URLConnectiontiene un conjunto muy limitado de tipos de contenido que reconoce. Por ejemplo, no puede detectar application/pdf.
kpentchev
3
Solo lo deja bloqueado porque no has dejado ninguna forma de cerrarlo. Desconectar la URLConnection lo desbloquearía.
Marqués de Lorne
1
tanto guessContentTypeFromStream como guessContentTypeFromName NO reconocen, por ejemplo, mp4
Hartmut P.
3
guessContentTypeFromName()utiliza el $JAVA_HOME/lib/content-types.propertiesarchivo predeterminado puede agregar su propio archivo extendido cambiando la propiedad del sistemaSystem.setProperty("content.types.user.table","/lib/path/to/your/property/file");
Rasika Perera
50

La API JAF es parte de JDK 6. Mira el javax.activationpaquete.

Las clases más interesantes son javax.activation.MimeType- un titular de tipo MIME real - y javax.activation.MimetypesFileTypeMap- clase cuya instancia puede resolver el tipo MIME como Cadena para un archivo:

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
fuente
44
Desafortunadamente, como getContentType(File)indica javadoc for : Devuelve el tipo MIME del objeto de archivo. La implementación en esta clase llama getContentType(f.getName()).
Matyas
3
Y recuerde que puede ampliar esta funcionalidad con el archivo META-INF / mime.types, por lo que es perfecto si se ve obligado a usar Java 6. docs.oracle.com/javaee/5/api/javax/activation/…
Chexpir
8
puede omitir la creación de un nuevo objetoMimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file)
akostadinov
Gracias por tu respuesta. Está funcionando con éxito para mí.
Radadiya Nikunj
Pero aún devuelve el tipo de contenido solo en función del nombre del archivo. Y esto es especialmente peligroso para los archivos cargados por los usuarios.
Sergey Ponomarev
47

Con Apache Tika solo necesita tres líneas de código :

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

Si tiene una consola maravillosa, simplemente pegue y ejecute este código para jugar con él:

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

Tenga en cuenta que sus API son ricas, puede analizar "cualquier cosa". A partir de tika-core 1.14, tiene:

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)

Vea las apidocs para más información.

lifeisfoo
fuente
1
No funciona para csv. wtf? stackoverflow.com/questions/46960231/…
gstackoverflow
1
Una cosa mala de Tika, mucha hinchazón de dependencia. ¡Aumentó el tamaño de mi tarro en 54MB!
helmy
1
@helmyTika 1.17 es independiente y solo tiene 648 KB de tamaño.
Sainan
... o simplemente new Tika().detect(file.toPath())para la detección basada en la extensión del archivo en lugar de la detección basada en el contenido del archivo
Lu55
Los documentos de @ Lu55 dicen que todavía usa el contenido del documento. Creo que quieres decir new Tika().detect(file.getPath()), que solo usa la extensión de archivo
delucasvb
31

Apache Tika ofrece en tika-core una detección de tipo mime basada en marcadores mágicos en el prefijo de transmisión. tika-coreno busca otras dependencias, lo que lo hace tan liviano como la utilidad de detección de tipo Mime actualmente no mantenida .

Ejemplo de código simple (Java 7), usando las variables theInputStreamytheFileName

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

Tenga en cuenta que MediaType.detect (...) no se puede usar directamente ( TIKA-1120 ). Se proporcionan más sugerencias en https://tika.apache.org/0.10/detection.html .

koppor
fuente
1
También Metadata.RESOURCE_NAME_KEYse puede omitir +1 (si no tiene ninguno o no puede confiar en el nombre original), pero en ese caso obtendrá resultados incorrectos en algunos casos (documentos de oficina, por ejemplo).
user1516873
Tiene algunos problemas para detectar XLSX si no hay una extensión en el nombre de archivo ... pero esta solución es simple y elegante.
Oscar Pérez
23

Si es un desarrollador de Android, puede usar una clase de utilidad android.webkit.MimeTypeMapque asigna tipos MIME a extensiones de archivo y viceversa.

El siguiente fragmento de código puede ayudarlo.

private static String getMimeType(String fileUrl) {
    String extension = MimeTypeMap.getFileExtensionFromUrl(fileUrl);
    return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
Pawan
fuente
3
Esto también funciona si se prueba con rutas de archivos locales como "/sdcard/path/to/video.extension". El problema es que si el archivo local contiene espacio en su ruta, siempre devuelve nulo
nmxprime
17

De roseindia :

FileNameMap fileNameMap = URLConnection.getFileNameMap();
String mimeType = fileNameMap.getContentTypeFor("alert.gif");
AlikElzin-kilaka
fuente
77
Quien haya rechazado la respuesta, agregue un comentario para que yo (y otros) puedan aprender a publicar mejores respuestas.
AlikElzin-kilaka
3
No lo rechacé, pero getFileNameMap no funciona para muchos tipos de archivos básicos, por ejemplo, 'bmp'. También URLConnection.guessContentTypeFromName devuelve lo mismo
Ovidiu Buligan
55
Función muy incompleta. A partir de Java 7, las extensiones html, pdf y jpeg devuelven el tipo mime correcto, ¡pero js y css devuelven nulo!
djsumdog
Probé con 'webm' y volvió nulo.
Henrique Rocha
16

Si está atascado con Java 5-6, entonces esta clase de utilidad del producto de código abierto servo .

Solo necesitas esta función

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

Sondea los primeros bytes del contenido y devuelve los tipos de contenido en función de ese contenido y no por extensión de archivo.

Ovidiu Buligan
fuente
Trabajé para los tipos de archivo simples, populares y pocos que necesitaba :)
user489041
13

Me preguntaba cómo la mayoría de las personas obtienen un tipo MIME de un archivo en Java.

He publicado mi paquete Java SimpleMagic que permite la determinación del tipo de contenido (tipo mime) a partir de archivos y conjuntos de bytes. Está diseñado para leer y ejecutar los archivos mágicos de comando del archivo Unix (1) que forman parte de la mayoría de las configuraciones de ~ Unix OS.

Intenté Apache Tika pero es enorme con toneladas de dependencias, URLConnectionno usa los bytes de los archivos y MimetypesFileTypeMaptambién solo mira los nombres de los archivos.

Con SimpleMagic puedes hacer algo como:

// 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
fuente
1
Probado en múltiples archivos de imagen. Todos tenían extensión renombrada. Su biblioteca impresionante lo manejó correctamente. Por supuesto, su luz también :).
saurabheights
1
Sí, esto funciona bien. Y para aquellos que necesitan usar esta solución dentro de Android, simplemente pueden incluir lo siguiente en el archivo build.gradle: compile ('com.j256.simplemagic: simplemagic: 1.10')
jkincali
1
¡Esta es una gran solución! ¡Gracias!
javydreamercsw
5

Para contribuir con mis 5 centavos:

TL, DR

Uso MimetypesFileTypeMap y agrego cualquier mime que no está allí y lo necesito específicamente, en el archivo mime.types.

Y ahora, la lectura larga:

En primer lugar, la lista de tipos MIME es enorme , consulte aquí: https://www.iana.org/assignments/media-types/media-types.xhtml

Me gusta usar las instalaciones estándar proporcionadas por JDK primero, y si eso no funciona, iré a buscar otra cosa.

Determinar el tipo de archivo desde la extensión del archivo

Desde 1.6, Java tiene MimetypesFileTypeMap, como se señala en una de las respuestas anteriores, y es la forma más sencilla de determinar el tipo mime:

new MimetypesFileTypeMap().getContentType( fileName );

En su implementación de vainilla esto no hace mucho (es decir, funciona para .html pero no para .png). Sin embargo, es muy simple agregar cualquier tipo de contenido que pueda necesitar:

  1. Cree un archivo llamado 'mime.types' en la carpeta META-INF de su proyecto
  2. Agregue una línea para cada tipo de mime que necesite y la implementación predeterminada no proporciona (hay cientos de tipos de mime y la lista crece a medida que pasa el tiempo).

Las entradas de ejemplo para archivos png y js serían:

image/png png PNG
application/javascript js

Para el formato de archivo mime.types, vea más detalles aquí: https://docs.oracle.com/javase/7/docs/api/javax/activation/MimetypesFileTypeMap.html

Determinar el tipo de archivo a partir del contenido del archivo

Desde 1.7, Java tiene java.nio.file.spi.FileTypeDetector , que define una API estándar para determinar un tipo de archivo en forma específica de implementación .

Para buscar el tipo mime para un archivo, simplemente use Archivos y haga esto en su código:

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

La definición de API proporciona recursos que admiten la determinación del tipo de archivo MIME a partir del nombre del archivo o del contenido del archivo (bytes mágicos). Es por eso que el método probeContentType () arroja IOException, en caso de que una implementación de esta API utilice la ruta que se le proporciona para intentar abrir el archivo asociado.

Nuevamente, la implementación vainilla de esto (la que viene con JDK) deja mucho que desear.

En un mundo ideal en una galaxia muy, muy lejana, todas estas bibliotecas que intentan resolver este problema de tipo de archivo a mimo simplemente implementarían java.nio.file.spi.FileTypeDetector , colocarías el jar de la biblioteca de implementación preferida archivo en su classpath y eso sería todo.

En el mundo real, en el que necesita la sección TL, DR, debe encontrar la biblioteca con la mayoría de las estrellas al lado de su nombre y usarla. Para este caso en particular, no necesito uno (todavía;)).

nidalpres
fuente
3

Intenté varias formas de hacerlo, incluidas las primeras que dijo @Joshua Fox. Pero algunos no reconocen los tipos MIME frecuentes, como los archivos PDF, y otros no pueden ser confiables con archivos falsos (intenté con un archivo RAR con la extensión cambiada a TIF). La solución que encontré, como también lo dijo @Joshua Fox de manera superficial, es usar MimeUtil2 , así:

MimeUtil2 mimeUtil = new MimeUtil2();
mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
String mimeType = MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(file)).toString();
ricardoc
fuente
55
No tuve ningún éxito con MimeUtil2: casi todo volvió como aplicación / octeto-flujo. Usé MimeUtil.getMimeTypes () con mucho más éxito después de inicializar con `MimeUtil.registerMimeDetector (" eu.medsea.mimeutil.detector.MagicMimeMimeDetector "); MimeUtil.registerMimeDetector ("eu.medsea.mimeutil.detector.ExtensionMimeDetector"); MimeUtil.registerMimeDetector ("eu.medsea.mimeutil.detector.OpendesktopMimeDetector"); `
Brian Pipa
2
Gracias por la solución de trabajo. La documentación de mime-util no es muy clara acerca de cómo instanciar la clase de utilidad. Finalmente lo puse en funcionamiento, pero reemplazó la cadena de nombre de clase con la clase real. MimeUtil.registerMimeDetector (ExtensionMimeDetector.class.getName ()); String mimeType = MimeUtil.getMostSpecificMimeType (MimeUtil.getMimeTypes (nombre de archivo)). ToString ();
Rob Juurlink
2

Es mejor utilizar la validación de dos capas para cargar archivos.

Primero puede verificar el mimeType y validarlo.

En segundo lugar, debe buscar convertir los primeros 4 bytes de su archivo a hexadecimal y luego compararlo con los números mágicos. Entonces será una forma realmente segura de verificar las validaciones de archivos.

javacreed
fuente
2

Esta es la forma más simple que encontré para hacer esto:

byte[] byteArray = ...
InputStream is = new BufferedInputStream(new ByteArrayInputStream(byteArray));
String mimeType = URLConnection.guessContentTypeFromStream(is);
madx
fuente
¡La mejor solución!
Sherzod
2

Si está trabajando con un Servlet y el contexto del servlet está disponible para usted, puede usar:

getServletContext().getMimeType( fileName );
Ramishka Dasanayaka
fuente
1
¿Qué es getServletContext?
e-info128
1

en primavera archivo MultipartFile ;

org.springframework.web.multipart.MultipartFile

file.getContentType();

Ahmad R. Nazemi
fuente
0

Si trabaja en Linux OS, hay una línea de comando 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 "";
}

Luego

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

mimetype("/var/www/ggg/au.mp3") //  'audio/mp3'
Abdennour TOUMI
fuente
2
Esto funcionará, pero es una mala práctica de IMO, ya que vincula su código a un sistema operativo específico y requiere que la utilidad externa esté presente en el sistema que lo ejecuta. No me malinterpretes; es una solución totalmente válida, pero rompe la portabilidad, que es una de las principales razones para usar Java en primer lugar ...
ToVine
@ToVine: Para que conste, voy a estar respetuosamente en desacuerdo. No todos los programas Java deben ser portátiles. Deje que el contexto y el programador tomen esa decisión. en.wikipedia.org/wiki/Java_Native_Interface
Zahnon
0

Después de probar varias otras bibliotecas, me decidí por 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
fuente
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
fuente
Este método Files.probeContentType (String) está disponible desde la versión 1.7 de JDK y funciona muy bien para mí.
Reza Rahimi
Gracias, solo que no entiendo por qué algunos usuarios rechazaron votar)))
Vazgen Torosyan
Para nada, tal vez tengan una versión anterior de JDK :)))
Reza Rahimi
0

Puede hacerlo con una sola línea: MimetypesFileTypeMap (). GetContentType (nuevo archivo ("filename.ext")) . Mira el código de prueba completo (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")));
    }
}

Este código produce el siguiente resultado: text / plain

Cassio Seffrin
fuente
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
fuente
44
Si bien este código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación.
Shree
0

Lo hice con el siguiente código.

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
fuente
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>

y dos líneas de código.

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

Captura de pantalla a continuación

ingrese la descripción de la imagen aquí

Pratik Gaurav
fuente