在Java中获取文件的Mime类型


336

我只是想知道大多数人如何从Java文件中获取mime类型?到目前为止,我已经尝试了两个工具:JMimeMagicMime-Util

第一个给我内存异常,第二个没有正确关闭其流。我只是想知道其他人是否拥有他们使用和正常工作的方法/库?



我使用了在此处发布答案的课程:stackoverflow.com/a/10140531/293280
Joshua Pinter 2014年

3
蒂卡现在应该是答案。下面的其他答案说明了Tika的许多依赖关系,但tika-core却没有。
javamonkey79 2015年

当我们使用TIka时,@ javamonkey79会隐藏文件,并且不再可用。字符串contentType = tika.detect(is)。
Cool Techie

Answers:


326

在Java 7中,您现在可以使用Files.probeContentType(path)


62
请注意,Files.probeContentType(Path)在多个操作系统上存在错误,并且已经提交了许多错误报告。我在ubuntu上运行软件时遇到问题,但在Windows上无法运行。似乎在Windows上Files.probeContentType(Path)总是返回null。这不是我的系统,所以我没有检查JRE或Windows版本。这对Java 7中的Windows 7或与Oracle JRE 8可能
白银

13
我在OS X 10.9上运行和我null出去.xml.png.xhtml文件。我不知道我是否在做严重的错误,但这似乎很糟糕。

36
一个主要的限制是文件必须存在于文件系统上。这不与数据流或一个字节阵列等工作
Necreaux

3
当我从名称中删除扩展名时,此方法无法返回mime类型。例如,如果名称为test.mp4,则将其更改为“ test”,并且该方法返回null。此外,我将电影扩展名更改为png,否则它将返回png mime类型
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
完美的解决方案-对我有很大帮助!包裹FileInputStreamBufferedInputStream是关键的部分-否则guessContentTypeFromStream返回null(通过InputStream实例应该支持引号)
尤里Nakonechnyy

11
但是,URLConnection它可以识别的内容类型非常有限。例如,它无法检测到application/pdf
kpentchev

3
它只会使它处于锁定状态,因为您没有办法关闭它。断开URLConnection会对其进行解锁。
2014年

1
既guessContentTypeFromStream也不guessContentTypeFromName不承认例如MP4
哈特穆特·体育

3
guessContentTypeFromName()使用默认$JAVA_HOME/lib/content-types.properties文件。您可以通过更改系统属性来添加自己的扩展文件System.setProperty("content.types.user.table","/lib/path/to/your/property/file");
Rasika Perera

50

JAF API是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())
Matyas

3
记住,您可以使用META-INF / mime.types文件扩展此功能,因此如果您被迫使用Java 6,它是完美的选择。docs.oracle.com
javaee/

7
您可以在MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file)
akostadinov

感谢您的回答。它为我成功地工作。
Radadiya Nikunj '18

但是它仍然仅基于文件名返回内容类型。这对于用户上传的文件尤其危险。
谢尔盖·波诺马列夫

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
关于Tika的一件坏事,很多依赖项膨胀。它使我的jar大小增加了54MB!
helmy

1
@helmyTika 1.17是独立的,只有648 KB大。
塞南

...或仅new Tika().detect(file.toPath())用于基于文件扩展名的检测,而不是基于文件内容的检测
Lu55,2018年

@ Lu55文档说仍然使用文档内容。我认为您的意思是new Tika().detect(file.getPath()),它仅使用文件扩展名
delucasvb

31

阿帕奇·蒂卡(Apache Tika)tika-core中提供了基于流前缀中的魔术标记的mime类型检测。tika-core不会获取其他依赖关系,这使其与当前未维护的Mime Type Detection Utility一样轻巧。

简单代码示例(Java 7),使用变量theInputStreamtheFileName

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可以省略(如果您没有名字或不能依靠原始名称),但是在某些情况下(例如办公文件),您会得到错误的结果。
user1516873 '17

如果文件名没有扩展名,则在检测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
nmxprime15

17

roseindia

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

7
谁投票否决了答案,请添加评论,以便我(和其他人)可以学习发布更好的答案。
AlikElzin-kilaka 2013年

3
我没有否决您的意见,但是getFileNameMap不适用于许多基本文件类型,例如'bmp'。同样,URLConnection.guessContentTypeFromName返回相同的内容
Ovidiu Buligan

5
功能非常不完整。从Java 7开始,html,pdf和jpeg扩展名返回正确的mime类型,但是js和css返回null!
djsumdog 2014年

我使用“ webm”进行了测试,结果返回null。
Henrique Rocha

16

如果您坚持使用Java 5-6,那么来自伺服开源产品的此实用程序类。

您只需要此功能

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

它探测内容的前几个字节,并根据该内容而不是文件扩展名返回内容类型。


适用于我需要的简单,流行和少数文件类型:)
user489041 2016年

13

我只是想知道大多数人如何从Java文件中获取mime类型?

我已经发布了SimpleMagic Java软件包,该软件包允许从文件和字节数组确定内容类型(MIME类型)。它旨在读取和运行Unix file(1)命令魔术文件,这些文件是大多数〜Unix OS配置的一部分。

我试过的Apache提卡,但它是巨大的与吨的依赖性,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(我特别需要它)添加到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. 在项目的META-INF文件夹中创建名为“ mime.types”的文件
  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(),如果此API的实现使用提供给它的Path实际尝试打开与其关联的文件,则方法将引发IOException。

再次,香草实施这个(附带JDK中的一个)极不理想很多。

在一个遥远星系中的理想世界中,所有试图解决此文件到MIME类型问题的库都将简单地实现java.nio.file.spi.FileTypeDetector,您将放入首选实现库的jar中文件到您的类路径,就是这样。

在现实世界中,需要TL,DR部分的地方,您应该在其名称旁边找到带有最多星星的图书馆并使用它。对于这种特殊情况,我不需要一个(但;))。


3

我尝试了几种方法,包括@Joshua Fox说的第一种方法。但是有些不能识别频繁的模仿类型,例如PDF文件,而另一些不能被伪造文件信任(我尝试将扩展名更改为TIF的RAR文件)。我发现的解决方案(如@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.registerMimeDetector(“ eu.medsea.mimeutil.detector.MagicMimeMimeDetector”)初始化后,我使用MimeUtil.getMimeTypes()获得了更大的成功。MimeUtil.registerMimeDetector(“ eu.medsea.mimeutil.detector.ExtensionMimeDetector”); MimeUtil.registerMimeDetector(“ eu.medsea.mimeutil.detector.OpendesktopMimeDetector”); `
Brian Pipa

2
感谢您的解决方案。mime-util的文档对于如何实例化实用程序类不是很清楚。最终启动并运行它,但是用实际的类替换了类名字符串。MimeUtil.registerMimeDetector(ExtensionMimeDetector.class.getName()); 字符串mimeType = MimeUtil.getMostSpecificMimeType(MimeUtil.getMimeTypes(filename))。toString();
罗伯·尤里林克

2

最好使用两层验证来上传文件。

首先,您可以检查mimeType并对其进行验证。

其次,您应该将文件的前4个字节转换为十六进制,然后将其与幻数进行比较。然后,这将是检查文件验证的真正安全方法。


2

这是我发现的最简单的方法:

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

最好的解决方案!
谢尔佐德



0

如果您在Linux OS上工作,则有一个命令行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是一个不好的做法,因为它将您的代码绑定到特定的OS,并且要求运行它的系统中存在外部实用程序。不要误会我的意思;这是一个完全有效的解决方案,但是却破坏了可移植性-这是首先使用Java的主要原因之一……
ToVine 2015年

@ToVine:出于记录,我将表示不同意。并非每个Java程序都必须具有可移植性。让上下文和程序员做出决定。en.wikipedia.org/wiki/Java_Native_Interface
Zahnon '18

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

从JDK版本1.7开始,此方法Files.probeContentType(String)就可用了,它对我来说非常有用。
Reza Rahimi

谢谢,只有我不明白为什么有些用户投了反对票)))
Vazgen Torosyan

一点也不,也许他们有一个JDK的早期版本:)))
Reza Rahimi

0

您只需要一行即可完成: MimetypesFileTypeMap()。getContentType(new File(“ filename.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")));
    }
}

此代码产生以下输出:text / plain


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
尽管此代码可以解决问题,但提供说明确实有助于提高您的帖子质量。
Shree

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

下面的屏幕截图

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.