Obtendo o tipo MIME de um arquivo em Java

336

Eu só estava me perguntando como a maioria das pessoas busca um tipo MIME de um arquivo em Java? Até agora eu tentei dois utilitários: JMimeMagic& Mime-Util.

O primeiro me deu exceções de memória, o segundo não fecha seus fluxos corretamente. Eu só estava me perguntando se alguém mais tinha um método / biblioteca que eles usaram e funcionaram corretamente?

Lee Theobald
fonte
4
Uma boa visão geral das bibliotecas disponíveis é fornecida em rgagnon.com/javadetails/java-0487.html
koppor
Eu usei a classe que foi postado como uma resposta aqui: stackoverflow.com/a/10140531/293280
Joshua Pinter
3
Tika deve ser a resposta agora. As outras respostas abaixo esclarecem muitas dependências com o Tika, mas não vejo nenhuma com o tika-core.
javamonkey79
@ javamonkey79 quando usamos o TIka, ele cobre o arquivo e não é mais utilizável. String contentType = tika.detect (is).
Cool Techie

Respostas:

326

No Java 7, agora você pode apenas usar Files.probeContentType(path).

Chris Mowforth
fonte
62
Esteja ciente de que Files.probeContentType (Path) está com erros em vários sistemas operacionais e muitos relatórios de erros foram arquivados. Eu tive um problema com o software que trabalha no ubuntu, mas falha no Windows. Parecia que no Windows Files.probeContentType (Path) sempre retornava nulo. Não era meu sistema, então não verifiquei a versão do JRE ou do Windows. Foi o Windows 7 ou 8, provavelmente, com o Oracle JRE para Java 7.
prata
13
Estou correndo em OS X 10.9 e eu fico nullfora para .xml, .pnge .xhtmlarquivos. Não sei se estou fazendo algo terrivelmente errado, mas isso parece terrível.
36
Uma grande limitação disso é que o arquivo deve existir no sistema de arquivos. Isso não funciona com um fluxo ou uma matriz de bytes, etc.
Necreaux 31/03
3
este método não pode retornar MIME tipo de quando eu remover extensão do exmaple nome.Por se o nome é test.mp4 i transformá-lo em "teste" e método retorna null.Also i extensão filme mudança para png etc ele retorna png tipo mime
Sarkhan
10
Isso é inútil se o arquivo tiver uma extensão ausente ou incorreta.
shmosel 15/07/16
215

Infelizmente,

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

não funciona, pois esse uso de URL deixa um arquivo bloqueado, de modo que, por exemplo, não pode ser excluído.

No entanto, você tem o seguinte:

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

e também o seguinte, que tem a vantagem de ir além do mero uso da extensão de arquivo e dar uma olhada no conteúdo

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

No entanto, conforme sugerido pelo comentário acima, a tabela interna de tipos MIME é bastante limitada, não incluindo, por exemplo, MSWord e PDF. Portanto, se você deseja generalizar, precisará ir além das bibliotecas internas, usando, por exemplo, o Mime-Util (que é uma ótima biblioteca, usando extensão de arquivo e conteúdo).

Joshua Fox
fonte
8
Solução perfeita - me ajudou muito! Envolvendo FileInputStreamem BufferedInputStreamé parte crucial - caso contrário, guessContentTypeFromStreamretorna null(passou InputStreaminstância deve apoiar marcas)
Yuriy Nakonechnyy
11
Howerver, URLConnectionpossui um conjunto muito limitado de tipos de conteúdo que ele reconhece. Por exemplo, não é capaz de detectar application/pdf.
kpentchev
3
Apenas o deixa bloqueado porque você não tem como fechá-lo. Desconectar a URLConnection a desbloqueia.
Marquês de Lorne
11
tanto guessContentTypeFromStream nem guessContentTypeFromName não reconhecem por exemplo mp4
Hartmut P.
3
guessContentTypeFromName()usa $JAVA_HOME/lib/content-types.propertiesarquivo padrão . você pode adicionar seu próprio arquivo estendida alterando propriedade do sistemaSystem.setProperty("content.types.user.table","/lib/path/to/your/property/file");
Rasika Perera
50

A API do JAF faz parte do JDK 6. Veja o javax.activationpacote.

As classes mais interessantes são javax.activation.MimeType- um detentor do tipo MIME real - e javax.activation.MimetypesFileTypeMap- cuja instância pode resolver o tipo MIME como String para um arquivo:

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
fonte
4
Infelizmente, como o javadoc para getContentType(File)estados: Retorna o tipo MIME do objeto de arquivo. A implementação nesta classe chama getContentType(f.getName()).
Matyas 24/10
3
E lembre-se de que você pode estender essa funcionalidade com o arquivo META-INF / mime.types, para que seja perfeito se você for forçado a usar o Java 6. docs.oracle.com/javaee/5/api/javax/activation/…
Chexpir
8
você pode pular a criação de um novo objeto porMimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file)
akostadinov
Obrigado pela sua resposta. Está trabalhando com sucesso para mim.
Radadiya Nikunj
Mas ele ainda retorna o tipo de conteúdo apenas com base no nome do arquivo. E isso é especialmente perigoso para arquivos enviados por usuários.
Sergey Ponomarev
47

Com o Apache Tika, você precisa de apenas três linhas de código :

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

Se você tem um console legal, basta colar e executar este código para brincar com ele:

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

Lembre-se de que suas APIs são ricas; ele pode analisar "qualquer coisa". No tika-core 1.14, você tem:

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)

Veja os apidocs para mais informações.

lifeisfoo
fonte
11
Não funciona para CSV. wtf? stackoverflow.com/questions/46960231/…
gstackoverflow 26/17
11
Uma coisa ruim sobre Tika, muita dependência inchaço. Aumentou o tamanho do meu jar em 54MB !!!
helmy
11
@helmyTika 1.17 é autônomo e tem apenas 648 KB de tamanho.
Sainan
... ou apenas new Tika().detect(file.toPath())para a detecção do arquivo de extensão com base em vez de detecção com base no conteúdo do arquivo
Lu55
Os documentos do @ Lu55 dizem que ainda usa o conteúdo do documento. Eu acho que você quer dizer new Tika().detect(file.getPath()), que só usa a extensão do arquivo
delucasvb
31

O Apache Tika oferece no tika-core uma detecção do tipo MIME com base em marcadores mágicos no prefixo do fluxo. tika-corenão busca outras dependências, o que a torna tão leve quanto o Mime Type Detection Utility atualmente não mantido .

Exemplo de código simples (Java 7), usando as variáveis theInputStreametheFileName

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

Observe que o MediaType.detect (...) não pode ser usado diretamente ( TIKA-1120 ). Mais dicas são fornecidas em https://tika.apache.org/0.10/detection.html .

koppor
fonte
11
O +1 também Metadata.RESOURCE_NAME_KEYpode ser omitido (se você não possui ou não pode confiar no nome original), mas nesse caso você obtém resultado errado em alguns casos (documentos do escritório, por exemplo).
user1516873
Ele tem alguns problemas ao detectar o XLSX se não houver extensão no nome do arquivo ... mas esta solução é simples e elegante.
Oscar Pérez
23

Se você é um desenvolvedor Android, pode usar uma classe de utilitário android.webkit.MimeTypeMapque mapeia os tipos MIME para extensões de arquivo e vice-versa.

O seguinte snippet de código pode ajudá-lo.

private static String getMimeType(String fileUrl) {
    String extension = MimeTypeMap.getFileExtensionFromUrl(fileUrl);
    return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
Pawan
fonte
3
Isso também funciona se tentado com caminhos de arquivos locais como "/sdcard/path/to/video.extension". O problema é se o arquivo local contém espaço em seu caminho, ele sempre retorna null
nmxprime
17

De roseindia :

FileNameMap fileNameMap = URLConnection.getFileNameMap();
String mimeType = fileNameMap.getContentTypeFor("alert.gif");
AlikElzin-kilaka
fonte
7
Quem votou negativamente na resposta, adicione um comentário para que eu (e outros) possamos aprender a postar respostas melhores.
AlikElzin-Kilaka
3
Não votei em contrário, mas getFileNameMap não funciona para muitos tipos básicos de arquivos, por exemplo 'bmp'. Também URLConnection.guessContentTypeFromName retorna a mesma coisa
Ovidiu Buligan
5
Função muito incompleta. A partir do Java 7, as extensões html, pdf e jpeg retornam o tipo mime correto, mas js e css retornam nulos!
precisa saber é o seguinte
Eu testei com 'webm' e ele retornou nulo.
Henrique Rocha
16

Se você estiver com o java 5-6, então esta classe de utilitário do produto de código-fonte aberto servoy .

Você só precisa desta função

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

Ele investiga os primeiros bytes do conteúdo e retorna os tipos de conteúdo com base nesse conteúdo e não por extensão de arquivo.

Ovidiu Buligan
fonte
Trabalhei para os tipos de arquivos simples, populares e de que eu precisava :) #
user489041:
13

Eu só estava me perguntando como a maioria das pessoas busca um tipo MIME de um arquivo em Java?

Publiquei meu pacote Java SimpleMagic , que permite a determinação do tipo de conteúdo (mime-type) a partir de arquivos e matrizes de bytes. Ele foi projetado para ler e executar os arquivos mágicos de comando do arquivo Unix (1) que fazem parte da maioria das configurações do ~ Unix OS.

Eu tentei o Apache Tika, mas é enorme, com toneladas de dependências, URLConnectionnão usa os bytes dos arquivos e MimetypesFileTypeMaptambém apenas olha os nomes dos arquivos.

Com o SimpleMagic, você pode fazer 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();
}
cinzento
fonte
11
Testei em vários arquivos de imagem. Todos tiveram extensão renomeada. Sua incrível biblioteca lidou com isso corretamente. Claro que a sua luz também :).
Saurabheights 12/08/16
11
Sim, isso funciona bem. E para aqueles que precisam usar esta solução dentro de Android, você pode simplesmente incluir o seguinte no arquivo build.gradle: compilação ( 'com.j256.simplemagic: simplemagic: 1.10')
jkincali
11
Esta é uma otima soluçao! Obrigado!
Javydreamercsw
5

Para checar meus 5 centavos:

TL, DR

Uso MimetypesFileTypeMap e adiciono qualquer mímica que não esteja lá e preciso especificamente dela, no arquivo mime.types.

E agora, a longa leitura:

Primeiro, a lista de tipos MIME é enorme , veja aqui: https://www.iana.org/assignments/media-types/media-types.xhtml

Gosto de usar as instalações padrão fornecidas pelo JDK primeiro e, se isso não funcionar, irei procurar outra coisa.

Determinar o tipo de arquivo da extensão do arquivo

Desde a versão 1.6, o Java possui MimetypesFileTypeMap, conforme apontado em uma das respostas acima, e é a maneira mais simples de determinar o tipo mime:

new MimetypesFileTypeMap().getContentType( fileName );

Na sua implementação de baunilha, isso não faz muito (ou seja, funciona para .html, mas não para .png). No entanto, é super simples adicionar qualquer tipo de conteúdo que você possa precisar:

  1. Crie um arquivo chamado 'mime.types' na pasta META-INF no seu projeto
  2. Adicione uma linha para cada tipo MIME que você precisa e a implementação padrão não fornece (existem centenas de tipos MIME e a lista aumenta à medida que o tempo passa).

As entradas de exemplo para arquivos png e js seriam:

image/png png PNG
application/javascript js

Para o formato de arquivo mime.types, veja mais detalhes aqui: https://docs.oracle.com/javase/7/docs/api/javax/activation/MimetypesFileTypeMap.html

Determinar o tipo de arquivo a partir do conteúdo do arquivo

Desde 1.7, Java possui java.nio.file.spi.FileTypeDetector , que define uma API padrão para determinar um tipo de arquivo de maneira específica de implementação .

Para buscar o tipo MIME para um arquivo, basta usar Arquivos e fazer isso no seu código:

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

A definição da API fornece recursos que suportam a determinação do tipo de mímica de arquivo a partir do nome do arquivo ou do conteúdo do arquivo (bytes mágicos). É por isso que o método probeContentType () lança IOException, caso uma implementação dessa API use o Path fornecido para tentar realmente abrir o arquivo associado a ela.

Novamente, a implementação baunilha disso (a que acompanha o JDK) deixa muito a desejar.

Em algum mundo ideal em uma galáxia muito distante, todas essas bibliotecas que tentam resolver esse problema de arquivo a mimo simplesmente implementariam java.nio.file.spi.FileTypeDetector , você deixaria o jar da biblioteca de implementação preferida arquivo em seu caminho de classe e seria isso.

No mundo real, aquele em que você precisa da seção TL, DR, você deve encontrar a biblioteca com mais estrelas ao lado do nome e usá-la. Para este caso em particular, não preciso de um (ainda;)).

nidalpres
fonte
3

Eu tentei várias maneiras de fazer isso, incluindo as primeiras ditas por Joshua Fox. Mas alguns não reconhecem tipos de mimet frequentes, como arquivos PDF, e outros não podem ser confiáveis ​​com arquivos falsos (tentei com um arquivo RAR com a extensão alterada para TIF). A solução que encontrei, como também é dito por Joshua Fox de uma maneira superficial, é usar o MimeUtil2 , assim:

MimeUtil2 mimeUtil = new MimeUtil2();
mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
String mimeType = MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(file)).toString();
ricardoc
fonte
5
Não tive sucesso com o MimeUtil2 - quase tudo voltou como application / octet-stream. Eu usei MimeUtil.getMimeTypes () com muito mais sucesso depois de inicializar com `MimeUtil.registerMimeDetector (" eu.medsea.mimeutil.detector.MagicMimeMimeMetDetector "); MimeUtil.registerMimeDetector ("eu.medsea.mimeutil.detector.ExtensionMimeDetector"); MimeUtil.registerMimeDetector ("eu.medsea.mimeutil.detector.OpendesktopMimeDetector"); `
Brian Pipa
2
Obrigado pela solução de trabalho. A documentação do mime-util não é muito clara sobre como instanciar a classe do utilitário. Finalmente o instalei e funcionou, mas substituiu a cadeia de nome de classe pela classe real. MimeUtil.registerMimeDetector (ExtensionMimeDetector.class.getName ()); String mimeType = MimeUtil.getMostSpecificMimeType (MimeUtil.getMimeTypes (nome do arquivo)). ToString ();
precisa saber é o seguinte
2

É melhor usar a validação em duas camadas para o upload de arquivos.

Primeiro você pode verificar o mimeType e validá-lo.

Segundo, você deve converter os 4 primeiros bytes do seu arquivo em hexadecimal e depois compará-lo com os números mágicos. Então será uma maneira realmente segura de verificar as validações de arquivo.

javacreed
fonte
2

Esta é a maneira mais simples que encontrei para fazer isso:

byte[] byteArray = ...
InputStream is = new BufferedInputStream(new ByteArrayInputStream(byteArray));
String mimeType = URLConnection.guessContentTypeFromStream(is);
madx
fonte
Muito melhor solução!
Sherzod 28/01
2

Se você estiver trabalhando com um Servlet e se o contexto do servlet estiver disponível, você poderá usar:

getServletContext().getMimeType( fileName );
Ramishka Dasanayaka
fonte
11
O que é getServletContext?
e-info128 30/04
1

no arquivo MultipartFile da primavera ;

org.springframework.web.multipart.MultipartFile

file.getContentType();

Ahmad R. Nazemi
fonte
0

Se você trabalha no sistema operacional Linux, há uma linha 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 "";
}

Então

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

mimetype("/var/www/ggg/au.mp3") //  'audio/mp3'
Abdennour TOUMI
fonte
2
Isso funcionará, mas é uma má prática da IMO, pois vincula seu código a um sistema operacional específico e exige que o utilitário externo esteja presente no sistema que o executa. Não me interpretem mal; é uma solução totalmente válida, mas quebra a portabilidade - que é um dos principais motivos para usar Java em primeiro lugar ...
ToVine
@ ToVine: Para constar, eu discordo respeitosamente. Nem todo programa Java precisa ser portátil. Deixe o contexto e o programador tomarem essa decisão. en.wikipedia.org/wiki/Java_Native_Interface
Zahnon
0

Depois de tentar várias outras bibliotecas, resolvi usar o 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
fonte
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
fonte
Este método Files.probeContentType (String) está disponível desde o JDK versão 1.7 e funciona muito bem para mim.
Reza Rahimi
Obrigado, só que eu não consigo entender por que alguns usuários fizeram para baixo voto)))
Vazgen Torosyan
Nem por isso, talvez eles tenham uma versão anterior do JDK :)))
Reza Rahimi
0

Você pode fazer isso com apenas uma linha: MimetypesFileTypeMap (). GetContentType (new File ("filename.ext")) . Veja o código de teste 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 produz a seguinte saída: text / plain

Cassio Seffrin
fonte
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
fonte
4
Embora esse código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem.
Shree
0

Eu fiz isso com o seguinte 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
fonte
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>

e duas linhas de código.

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

Captura de tela abaixo

insira a descrição da imagem aqui

Pratik Gaurav
fonte