ChatGPT解决这个技术问题 Extra ChatGPT

如何获取文件的媒体类型(MIME 类型)?

如何使用 Java 从文件中获取媒体类型(MIME 类型)?到目前为止,我已经尝试过 JMimeMagic 和 Mime-Util。第一个给了我内存异常,第二个没有正确关闭它的流。

您将如何探测文件以确定其实际类型(不仅仅是基于扩展名)?

rgagnon.com/javadetails/java-0487.html 中提供了有关可用库的完整概述
我在这里使用了作为答案发布的课程:stackoverflow.com/a/10140531/293280
蒂卡现在应该是答案。下面的其他答案说明了与 Tika 的许多依赖关系,但我没有看到与 tika-core 的依赖关系。
@javamonkey79 当我们使用 TIka 时,它会隐藏文件并且不再可用。字符串 contentType = tika.detect(is) 。

C
Chris Mowforth

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


请注意 Files.probeContentType(Path) 在多个操作系统上存在错误,并且已提交大量错误报告。我在 ubuntu 上运行的软件有问题,但在 windows 上失败了。似乎在 Windows Files.probeContentType(Path) 上总是返回 null。这不是我的系统,所以我没有检查 JRE 或 windows 版本。它是 Windows 7 或 8,可能带有用于 Java 7 的 oracle JRE。
我在 OS X 10.9 上运行,我为 .xml.png.xhtml 文件获取 null。我不知道我是否只是在做一些可怕的错误,但这似乎相当可怕。
一个主要的限制是文件必须存在于文件系统上。这不适用于流或字节数组等。
当我从名称中删除扩展名时,此方法无法返回 mime 类型。例如,如果名称为 test.mp4,我将其更改为“test”并且方法返回 null。此外,我将电影扩展名更改为 png 等,它返回 png mime 类型
如果文件的扩展名丢失或错误,这将毫无用处。
J
Joshua Fox

很遗憾,

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(这是一个很棒的库,同时使用文件扩展名和内容)。


完美的解决方案 - 帮了我很多!将 FileInputStream 包装到 BufferedInputStream 是关键部分 - 否则 guessContentTypeFromStream 返回 null(传递的 InputStream 实例应该支持标记)
但是,URLConnection 的内容类型非常有限,它确实可以识别。例如,它无法检测 application/pdf
它只会将其锁定,因为您无法关闭它。断开 URLConnection 将解锁它。
guessContentTypeFromStream 和guessContentTypeFromName 都不能识别例如mp4
guessContentTypeFromName() 使用默认的 $JAVA_HOME/lib/content-types.properties 文件。您可以通过更改系统属性 System.setProperty("content.types.user.table","/lib/path/to/your/property/file"); 添加您自己的扩展文件
l
lifeisfoo

使用 Apache Tika,您只需要 三行代码

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

如果您有一个 groovy 控制台,只需粘贴并运行此代码即可使用它:

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

有关详细信息,请参阅 the apidocs


关于 Tika 的一件坏事,很多依赖膨胀。它使我的 jar 大小增加了 54MB!!!
@helmyTika 1.17 是独立的,只有 648 KB 大。
...或者只是 new Tika().detect(file.toPath()) 用于基于文件扩展名的检测,而不是基于文件内容的检测
@Lu55 文档说仍然使用文档内容。我想你的意思是 new Tika().detect(file.getPath()),它只使用文件扩展名
A
Adam Hošek

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

不幸的是,正如 getContentType(File) 的 javadoc 所述:返回文件对象的 MIME 类型。此类中的实现调用 getContentType(f.getName())
请记住,您可以使用 META-INF/mime.types 文件扩展此功能,因此如果您被迫使用 Java 6,它是完美的。docs.oracle.com/javaee/5/api/javax/activation/…
您可以通过 MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file) 跳过创建新对象
但它仍然仅根据文件名返回内容类型。这对于用户上传的文件尤其危险。
例如,这不适用于 pdf 文件(返回 application/octet-stream)。
k
koppor

Apache Tikatika-core 中提供基于流前缀中的魔术标记的 mime 类型检测。 tika-core 不获取其他依赖项,这使其与当前未维护的 Mime Type Detection Utility 一样轻量级。

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

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/1.24/detection.html 提供了更多提示。


+1 Metadata.RESOURCE_NAME_KEY 也可以省略(如果您没有或不能依赖原始名称),但在这种情况下,在某些情况下您会得到错误的结果(例如办公文件)。
如果文件名上没有扩展名,它在检测 XLSX 时会遇到一些问题......但这个解决方案简单而优雅。
P
Pawan

如果您是 Android 开发人员,则可以使用实用程序类 android.webkit.MimeTypeMap,它将 MIME 类型映射到文件扩展名,反之亦然。

以下代码段可能会对您有所帮助。

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

如果尝试使用诸如“/sdcard/path/to/video.extension”之类的本地文件路径,这也适用。问题是如果本地文件在其路径中包含空格,它总是返回 null
A
AlikElzin-kilaka

roseindia

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

无论谁对答案投了反对票,请添加评论,以便我(和其他人)可以学会发布更好的答案。
我没有投票给你,但是 getFileNameMap 不适用于许多基本文件类型,例如“bmp”。 URLConnection.guessContentTypeFromName 也返回相同的东西
很不完整的功能。从 Java 7 开始,html、pdf 和 jpeg 扩展返回正确的 mime 类型,但 js 和 css 返回 null!
我用'webm'进行了测试,它返回了null。
G
Gray

如果您被 java 5-6 所困,那么 servoy open source product 中的这个实用程序类。

你只需要这个功能

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

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


G
Gray

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

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

我尝试了 Apache Tika,但它巨大 有大量依赖项,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();
}

在多个图像文件上对其进行了测试。所有扩展名都已重命名。你很棒的图书馆处理得很好。当然它也很轻:)。
是的,这很好用。对于那些需要在 Android 中使用此解决方案的人,您可以简单地在 build.gradle 文件中包含以下内容:compile('com.j256.simplemagic:simplemagic:1.10')
n
nidalpres

用我的 5 美分筹码:

总长,博士

我使用 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)。但是,添加您可能需要的任何内容类型都非常简单:

在项目的 META-INF 文件夹中创建名为“mime.types”的文件为您需要的每种 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 并在代码中执行此操作:

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

API 定义提供了支持从文件名或文件内容(魔术字节)确定文件 mime 类型的工具。这就是 probeContentType() 方法抛出 IOException 的原因,以防此 API 的实现使用提供给它的 Path 来实际尝试打开与其关联的文件。

同样,这个(JDK 附带的)的普通实现还有很多不足之处。

在遥远的星系中的某个理想世界中,所有这些试图解决这个文件到mime-type问题的库都将简单地实现java.nio.file.spi.FileTypeDetector,您可以将首选实现库的jar文件放入您的类路径中,然后会的。

在现实世界中,您需要 TL、DR 部分,您应该找到名称旁边有最多星号的库并使用它。对于这种特殊情况,我不需要一个(还;))。


S
Sathiamoorthy

简单和最佳选项从文件位置检索内容 mime 类型。

使用这个进口

import java.nio.file.Files;
import java.nio.file.Path;

代码

String type = Files.probeContentType(Path.of(imagePath));

F
Frakcool

我尝试了几种方法来做到这一点,包括@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();

我在使用 MimeUtil2 时完全没有成功——几乎所有东西都以应用程序/八位字节流的形式返回。在使用 MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector"); 初始化后,我使用 MimeUtil.getMimeTypes() 取得了更大的成功。 MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.ExtensionMimeDetector"); MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.OpendesktopMimeDetector"); `
感谢您的工作解决方案。 mime-util 的文档对于如何实例化实用程序类不是很清楚。最后让它启动并运行,但用实际的类替换了类名字符串。 MimeUtil.registerMimeDetector(ExtensionMimeDetector.class.getName());字符串 mimeType = MimeUtil.getMostSpecificMimeType(MimeUtil.getMimeTypes(filename)).toString();
m
madx

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

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

P
Pratik Gaurav

阿帕奇蒂卡。

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

下面的截图

https://i.stack.imgur.com/zGTrB.png


j
javacreed

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

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

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


C
Cassio Seffrin

您只需一行即可完成: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


R
Ramishka Dasanayaka

如果您正在使用 Servlet,并且您可以使用 Servlet 上下文,则可以使用:

getServletContext().getMimeType( fileName );

getServletContext 是什么?
S
Samuel Prevost

我找不到任何可以检查 video/mp4 MIME 类型的内容,因此我制定了自己的解决方案。我碰巧观察到 Wikipedia 是错误的,并且 00 00 00 18 66 74 79 70 69 73 6F 6D 文件签名不正确。第四个字节 (18) 和所有 70 (排除) 在其他有效的 mp4 文件中发生了很大变化。

此代码本质上是 URLConnection.guessContentTypeFromStream 代码的复制/粘贴,但针对 video/mp4 量身定制。

BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(content));
String mimeType = URLConnection.guessContentTypeFromStream(bis);

// Goes full barbaric and processes the bytes manually
if (mimeType == null){
    // These ints converted in hex ar:
    // 00 00 00 18 66 74 79 70 69 73 6F 6D
    // which are the file signature (magic bytes) for .mp4 files
    // from https://www.wikiwand.com/en/List_of_file_signatures
    // just ctrl+f "mp4"
    int[] mp4_sig = {0, 0, 0, 24, 102, 116, 121, 112};

    bis.reset();
    bis.mark(16);
    int[] firstBytes = new int[8];
    for (int i = 0; i < 8; i++) {
        firstBytes[i] = bis.read();
    }
    // This byte doesn't matter for the file signature and changes
    mp4_sig[3] = content[3];

    bis.reset();
    if (Arrays.equals(firstBytes, mp4_sig)){
        mimeType = "video/mp4";
    }
}

针对 10 个不同的 .mp4 文件成功测试。

编辑:Here is a useful link (if it is still online) where you can find samples of many types。我不拥有这些视频,也不知道谁拥有,但它们对于测试上述代码很有用。


M
MIsmail

实际上,Apache Tika detector Tika.detect(File) 是最好的选择,并且比 Files.probeContentType(path) 更准确。

检查此 great quick reference,其中包含示例和代码示例。


A
Ahmad R. Nazemi

在 Spring MultipartFile 文件中;

org.springframework.web.multipart.MultipartFile

file.getContentType();


D
Dave Jarvis

检测文件的 Media Type1 的解决方案包含以下部分:

文件签名列表(参见 Kessler 的列表、维基百科的列表和 Space Maker 的列表)

媒体类型列表

媒体类型到文件扩展名的映射

将文件签名与 File、Path 或 InputStream 数据源进行比较

如果您复制代码,请记住给予信用。

StreamMediaType.java

在下面的代码中,-1 表示跳过比较该索引处的字节; -2 表示文件类型签名的结尾。这会检测二进制格式,主要是图像和一些纯文本格式变体(HTML、SVG、XML)。该代码最多使用数据源标头中的前 11 个“魔术”字节。欢迎进行缩短逻辑的优化和改进。

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;

import static com.keenwrite.io.MediaType.*;
import static java.lang.System.arraycopy;

public class StreamMediaType {
  private static final int FORMAT_LENGTH = 11;
  private static final int END_OF_DATA = -2;

  private static final Map<int[], MediaType> FORMAT = new LinkedHashMap<>();

  static {
    //@formatter:off
    FORMAT.put( ints( 0x3C, 0x73, 0x76, 0x67, 0x20 ), IMAGE_SVG_XML );
    FORMAT.put( ints( 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A ), IMAGE_PNG );
    FORMAT.put( ints( 0xFF, 0xD8, 0xFF, 0xE0 ), IMAGE_JPEG );
    FORMAT.put( ints( 0xFF, 0xD8, 0xFF, 0xEE ), IMAGE_JPEG );
    FORMAT.put( ints( 0xFF, 0xD8, 0xFF, 0xE1, -1, -1, 0x45, 0x78, 0x69, 0x66, 0x00 ), IMAGE_JPEG );
    FORMAT.put( ints( 0x49, 0x49, 0x2A, 0x00 ), IMAGE_TIFF );
    FORMAT.put( ints( 0x4D, 0x4D, 0x00, 0x2A ), IMAGE_TIFF );
    FORMAT.put( ints( 0x47, 0x49, 0x46, 0x38 ), IMAGE_GIF );
    FORMAT.put( ints( 0x8A, 0x4D, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A ), VIDEO_MNG );
    FORMAT.put( ints( 0x25, 0x50, 0x44, 0x46, 0x2D, 0x31, 0x2E ), APP_PDF );
    FORMAT.put( ints( 0x38, 0x42, 0x50, 0x53, 0x00, 0x01 ), IMAGE_PHOTOSHOP );
    FORMAT.put( ints( 0x25, 0x21, 0x50, 0x53, 0x2D, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x2D ), APP_EPS );
    FORMAT.put( ints( 0x25, 0x21, 0x50, 0x53 ), APP_PS );
    FORMAT.put( ints( 0xFF, 0xFB, 0x30 ), AUDIO_MP3 );
    FORMAT.put( ints( 0x49, 0x44, 0x33 ), AUDIO_MP3 );
    FORMAT.put( ints( 0x3C, 0x21 ), TEXT_HTML );
    FORMAT.put( ints( 0x3C, 0x68, 0x74, 0x6D, 0x6C ), TEXT_HTML );
    FORMAT.put( ints( 0x3C, 0x68, 0x65, 0x61, 0x64 ), TEXT_HTML );
    FORMAT.put( ints( 0x3C, 0x62, 0x6F, 0x64, 0x79 ), TEXT_HTML );
    FORMAT.put( ints( 0x3C, 0x48, 0x54, 0x4D, 0x4C ), TEXT_HTML );
    FORMAT.put( ints( 0x3C, 0x48, 0x45, 0x41, 0x44 ), TEXT_HTML );
    FORMAT.put( ints( 0x3C, 0x42, 0x4F, 0x44, 0x59 ), TEXT_HTML );
    FORMAT.put( ints( 0x3C, 0x3F, 0x78, 0x6D, 0x6C, 0x20 ), TEXT_XML );
    FORMAT.put( ints( 0xFE, 0xFF, 0x00, 0x3C, 0x00, 0x3f, 0x00, 0x78 ), TEXT_XML );
    FORMAT.put( ints( 0xFF, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00 ), TEXT_XML );
    FORMAT.put( ints( 0x42, 0x4D ), IMAGE_BMP );
    FORMAT.put( ints( 0x23, 0x64, 0x65, 0x66 ), IMAGE_X_BITMAP );
    FORMAT.put( ints( 0x21, 0x20, 0x58, 0x50, 0x4D, 0x32 ), IMAGE_X_PIXMAP );
    FORMAT.put( ints( 0x2E, 0x73, 0x6E, 0x64 ), AUDIO_BASIC );
    FORMAT.put( ints( 0x64, 0x6E, 0x73, 0x2E ), AUDIO_BASIC );
    FORMAT.put( ints( 0x52, 0x49, 0x46, 0x46 ), AUDIO_WAV );
    FORMAT.put( ints( 0x50, 0x4B ), APP_ZIP );
    FORMAT.put( ints( 0x41, 0x43, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00 ), APP_ACAD );
    FORMAT.put( ints( 0xCA, 0xFE, 0xBA, 0xBE ), APP_JAVA );
    FORMAT.put( ints( 0xAC, 0xED ), APP_JAVA_OBJECT );
    //@formatter:on
  }

  private StreamMediaType() {
  }

  public static MediaType getMediaType( final Path path ) throws IOException {
    return getMediaType( path.toFile() );
  }

  public static MediaType getMediaType( final java.io.File file )
    throws IOException {
    try( final var fis = new FileInputStream( file ) ) {
      return getMediaType( fis );
    }
  }

  public static MediaType getMediaType( final InputStream is )
    throws IOException {
    final var input = new byte[ FORMAT_LENGTH ];
    final var count = is.read( input, 0, FORMAT_LENGTH );

    if( count > 1 ) {
      final var available = new byte[ count ];
      arraycopy( input, 0, available, 0, count );
      return getMediaType( available );
    }

    return UNDEFINED;
  }

  public static MediaType getMediaType( final byte[] data ) {
    assert data != null;

    final var source = new int[]{
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

    for( int i = 0; i < data.length; i++ ) {
      source[ i ] = data[ i ] & 0xFF;
    }

    for( final var key : FORMAT.keySet() ) {
      int i = -1;
      boolean matches = true;

      while( ++i < FORMAT_LENGTH && key[ i ] != END_OF_DATA && matches ) {
        matches = key[ i ] == source[ i ] || key[ i ] == -1;
      }

      if( matches ) {
        return FORMAT.get( key );
      }
    }

    return UNDEFINED;
  }

  private static int[] ints( final int... data ) {
    final var magic = new int[ FORMAT_LENGTH ];
    int i = -1;
    while( ++i < data.length ) {
      magic[ i ] = data[ i ];
    }

    while( i < FORMAT_LENGTH ) {
      magic[ i++ ] = END_OF_DATA;
    }

    return magic;
  }
}

媒体类型.java

根据 IANA Media Type 列表定义文件格式。请注意,文件扩展名映射在 MediaTypeExtension 中。 getExtension 函数依赖于 Apache 的 FilenameUtils 类。

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;

import static MediaType.TypeName.*;
import static MediaTypeExtension.getMediaType;
import static org.apache.commons.io.FilenameUtils.getExtension;

public enum MediaType {
  APP_ACAD( APPLICATION, "acad" ),
  APP_JAVA_OBJECT( APPLICATION, "x-java-serialized-object" ),
  APP_JAVA( APPLICATION, "java" ),
  APP_PS( APPLICATION, "postscript" ),
  APP_EPS( APPLICATION, "eps" ),
  APP_PDF( APPLICATION, "pdf" ),
  APP_ZIP( APPLICATION, "zip" ),
  FONT_OTF( "otf" ),
  FONT_TTF( "ttf" ),
  IMAGE_APNG( "apng" ),
  IMAGE_ACES( "aces" ),
  IMAGE_AVCI( "avci" ),
  IMAGE_AVCS( "avcs" ),
  IMAGE_BMP( "bmp" ),
  IMAGE_CGM( "cgm" ),
  IMAGE_DICOM_RLE( "dicom_rle" ),
  IMAGE_EMF( "emf" ),
  IMAGE_EXAMPLE( "example" ),
  IMAGE_FITS( "fits" ),
  IMAGE_G3FAX( "g3fax" ),
  IMAGE_GIF( "gif" ),
  IMAGE_HEIC( "heic" ),
  IMAGE_HEIF( "heif" ),
  IMAGE_HEJ2K( "hej2k" ),
  IMAGE_HSJ2( "hsj2" ),
  IMAGE_X_ICON( "x-icon" ),
  IMAGE_JLS( "jls" ),
  IMAGE_JP2( "jp2" ),
  IMAGE_JPEG( "jpeg" ),
  IMAGE_JPH( "jph" ),
  IMAGE_JPHC( "jphc" ),
  IMAGE_JPM( "jpm" ),
  IMAGE_JPX( "jpx" ),
  IMAGE_JXR( "jxr" ),
  IMAGE_JXRA( "jxrA" ),
  IMAGE_JXRS( "jxrS" ),
  IMAGE_JXS( "jxs" ),
  IMAGE_JXSC( "jxsc" ),
  IMAGE_JXSI( "jxsi" ),
  IMAGE_JXSS( "jxss" ),
  IMAGE_KTX( "ktx" ),
  IMAGE_KTX2( "ktx2" ),
  IMAGE_NAPLPS( "naplps" ),
  IMAGE_PNG( "png" ),
  IMAGE_PHOTOSHOP( "photoshop" ),
  IMAGE_SVG_XML( "svg+xml" ),
  IMAGE_T38( "t38" ),
  IMAGE_TIFF( "tiff" ),
  IMAGE_WEBP( "webp" ),
  IMAGE_WMF( "wmf" ),
  IMAGE_X_BITMAP( "x-xbitmap" ),
  IMAGE_X_PIXMAP( "x-xpixmap" ),
  AUDIO_BASIC( AUDIO, "basic" ),
  AUDIO_MP3( AUDIO, "mp3" ),
  AUDIO_WAV( AUDIO, "x-wav" ),
  VIDEO_MNG( VIDEO, "x-mng" ),
  TEXT_HTML( TEXT, "html" ),
  TEXT_MARKDOWN( TEXT, "markdown" ),
  TEXT_PLAIN( TEXT, "plain" ),
  TEXT_XHTML( TEXT, "xhtml+xml" ),
  TEXT_XML( TEXT, "xml" ),
  TEXT_YAML( TEXT, "yaml" ),

  /*
   * When all other lights go out.
   */
  UNDEFINED( TypeName.UNDEFINED, "undefined" );

  public enum TypeName {
    APPLICATION,
    AUDIO,
    IMAGE,
    TEXT,
    UNDEFINED,
    VIDEO
  }

  private final String mMediaType;
  private final TypeName mTypeName;
  private final String mSubtype;

  MediaType( final String subtype ) {
    this( IMAGE, subtype );
  }

  MediaType( final TypeName typeName, final String subtype ) {
    mTypeName = typeName;
    mSubtype = subtype;
    mMediaType = typeName.toString().toLowerCase() + '/' + subtype;
  }

  public static MediaType valueFrom( final File file ) {
    assert file != null;
    return fromFilename( file.getName() );
  }

  public static MediaType fromFilename( final String filename ) {
    assert filename != null;
    return getMediaType( getExtension( filename ) );
  }

  public static MediaType valueFrom( final Path path ) {
    assert path != null;
    return valueFrom( path.toFile() );
  }

  public static MediaType valueFrom( String contentType ) {
    if( contentType == null || contentType.isBlank() ) {
      return UNDEFINED;
    }

    var i = contentType.indexOf( ';' );
    contentType = contentType.substring(
      0, i == -1 ? contentType.length() : i );

    i = contentType.indexOf( '/' );
    i = i == -1 ? contentType.length() : i;
    final var type = contentType.substring( 0, i );
    final var subtype = contentType.substring( i + 1 );

    return valueFrom( type, subtype );
  }

  public static MediaType valueFrom(
    final String type, final String subtype ) {
    assert type != null;
    assert subtype != null;

    for( final var mediaType : values() ) {
      if( mediaType.equals( type, subtype ) ) {
        return mediaType;
      }
    }

    return UNDEFINED;
  }

  public boolean equals( final String type, final String subtype ) {
    assert type != null;
    assert subtype != null;

    return mTypeName.name().equalsIgnoreCase( type ) &&
      mSubtype.equalsIgnoreCase( subtype );
  }

  public boolean isType( final TypeName typeName ) {
    return mTypeName == typeName;
  }

  public String getSubtype() {
    return mSubtype;
  }
   
  @Override
  public String toString() {
    return mMediaType;
  }
}

MediaTypeExtension.java

最后一块拼图是 MediaType 到其已知和常见/流行的文件扩展名的映射。这允许基于文件扩展名的双向查找。

import static MediaType.*;
import static java.util.List.of;

public enum MediaTypeExtension {
  MEDIA_APP_ACAD( APP_ACAD, of( "dwg" ) ),
  MEDIA_APP_PDF( APP_PDF ),
  MEDIA_APP_PS( APP_PS, of( "ps" ) ),
  MEDIA_APP_EPS( APP_EPS ),
  MEDIA_APP_ZIP( APP_ZIP ),

  MEDIA_AUDIO_MP3( AUDIO_MP3 ),
  MEDIA_AUDIO_BASIC( AUDIO_BASIC, of( "au" ) ),
  MEDIA_AUDIO_WAV( AUDIO_WAV, of( "wav" ) ),

  MEDIA_FONT_OTF( FONT_OTF ),
  MEDIA_FONT_TTF( FONT_TTF ),

  MEDIA_IMAGE_APNG( IMAGE_APNG ),
  MEDIA_IMAGE_BMP( IMAGE_BMP ),
  MEDIA_IMAGE_GIF( IMAGE_GIF ),
  MEDIA_IMAGE_JPEG( IMAGE_JPEG,
                    of( "jpg", "jpe", "jpeg", "jfif", "pjpeg", "pjp" ) ),
  MEDIA_IMAGE_PNG( IMAGE_PNG ),
  MEDIA_IMAGE_PSD( IMAGE_PHOTOSHOP, of( "psd" ) ),
  MEDIA_IMAGE_SVG( IMAGE_SVG_XML, of( "svg" ) ),
  MEDIA_IMAGE_TIFF( IMAGE_TIFF, of( "tiff", "tif" ) ),
  MEDIA_IMAGE_WEBP( IMAGE_WEBP ),
  MEDIA_IMAGE_X_BITMAP( IMAGE_X_BITMAP, of( "xbm" ) ),
  MEDIA_IMAGE_X_PIXMAP( IMAGE_X_PIXMAP, of( "xpm" ) ),

  MEDIA_VIDEO_MNG( VIDEO_MNG, of( "mng" ) ),

  MEDIA_TEXT_MARKDOWN( TEXT_MARKDOWN, of(
    "md", "markdown", "mdown", "mdtxt", "mdtext", "mdwn", "mkd", "mkdown",
    "mkdn" ) ),
  MEDIA_TEXT_PLAIN( TEXT_PLAIN, of( "txt", "asc", "ascii", "text", "utxt" ) ),
  MEDIA_TEXT_R_MARKDOWN( TEXT_R_MARKDOWN, of( "Rmd" ) ),
  MEDIA_TEXT_R_XML( TEXT_R_XML, of( "Rxml" ) ),
  MEDIA_TEXT_XHTML( TEXT_XHTML, of( "xhtml" ) ),
  MEDIA_TEXT_XML( TEXT_XML ),
  MEDIA_TEXT_YAML( TEXT_YAML, of( "yaml", "yml" ) ),

  MEDIA_UNDEFINED( UNDEFINED, of( "undefined" ) );

  private final MediaType mMediaType;
  private final List<String> mExtensions;

  MediaTypeExtension( final MediaType mediaType ) {
    this( mediaType, of( mediaType.getSubtype() ) );
  }

  MediaTypeExtension(
    final MediaType mediaType, final List<String> extensions ) {
    assert mediaType != null;
    assert extensions != null;
    assert !extensions.isEmpty();

    mMediaType = mediaType;
    mExtensions = extensions;
  }

  public String getExtension() {
    return mExtensions.get( 0 );
  }

  public static MediaTypeExtension valueFrom( final MediaType mediaType ) {
    for( final var type : values() ) {
      if( type.isMediaType( mediaType ) ) {
        return type;
      }
    }

    return MEDIA_UNDEFINED;
  }

  boolean isMediaType( final MediaType mediaType ) {
    return mMediaType == mediaType;
  }

  static MediaType getMediaType( final String extension ) {
    final var sanitized = sanitize( extension );

    for( final var mediaType : MediaTypeExtension.values() ) {
      if( mediaType.isType( sanitized ) ) {
        return mediaType.getMediaType();
      }
    }

    return UNDEFINED;
  }

  private boolean isType( final String sanitized ) {
    for( final var extension : mExtensions ) {
      if( extension.equalsIgnoreCase( sanitized ) ) {
        return true;
      }
    }

    return false;
  }

  private static String sanitize( final String extension ) {
    return extension == null ? "" : extension.toLowerCase();
  }

  private MediaType getMediaType() {
    return mMediaType;
  }
}

用途:

// EXAMPLE -- Detect media type
//
final File image = new File( "filename.jpg" );
final MediaType mt = StreamMediaType.getMediaType( image );

// Tricky! The JPG could be a PNG in disguise.
if( mt.isType( MediaType.TypeName.IMAGE ) ) {

  if( mt == MediaType.IMAGE_PNG ) {
    // Nice try! Sneaky sneak.
  }
}

// EXAMPLE -- Get typical media type file name extension
//
final String ext = MediaTypeExtension.valueFrom( MediaType.IMAGE_SVG_XML ).getExtension();

// EXAMPLE -- Get media type from HTTP request
//
final var url = new URL( "https://localhost/path/file.ext" );
final var conn = (HttpURLConnection) url.openConnection();
final var contentType = conn.getContentType();
MediaType mediaType = valueFrom( contentType );

// Fall back to stream detection probe
if( mediaType == UNDEFINED ) {
  mediaType = StreamMediaType.getMediaType( conn.getInputStream() );
}

conn.disconnect();

你明白了。

简短的图书馆评论:

Apache Tika -- 600kb 膨胀,需要多行配置和多个 JAR 文件。

jMimeMagic -- 未完成,需要多行配置。

MimeUtil2——相当大,不能开箱即用。

FileTypeDetector -- 与 JDK 捆绑在一起,比山松甲虫出没的森林还要臭。

Files.probeContentType - 检测是特定于平台的,被认为是不可靠的(来源)。

MimetypesFileTypeMap -- 与activation.jar 捆绑在一起,使用文件扩展名。

用于测试的示例音频、视频和图像文件:

http://mirrors.standaloneinstaller.com/video-sample/

https://www.w3.org/People/mimasa/test/imgformat/

1 “MIME 类型”是一个已弃用的术语。


A
Abdennour TOUMI

如果您在 linux 操作系统上工作,则有一个命令行 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'

这将起作用,但 IMO 是一种不好的做法,因为它将您的代码绑定到特定的操作系统,并且需要外部实用程序存在于运行它的系统中。不要误会我的意思;这是一个完全有效的解决方案,但破坏了可移植性——这是首先使用 Java 的主要原因之一......
@ToVine:为了记录,我将恭敬地不同意。并非每个 Java 程序都必须是可移植的。让上下文和程序员做出决定。 en.wikipedia.org/wiki/Java_Native_Interface
K
K. Siva Prasad Reddy

在尝试了各种其他库后,我选择了 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);

V
Vazgen Torosyan
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;
}

此方法 Files.probeContentType(String) 从 JDK 1.7 版开始可用,它对我来说非常有用。
4
4b0
File file = new File(PropertiesReader.FILE_PATH);
MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
String mimeType = fileTypeMap.getContentType(file);
URLConnection uconnection = file.toURL().openConnection();
mimeType = uconnection.getContentType();

虽然此代码可以解决问题,但 including an explanation 确实有助于提高帖子的质量。
D
Dave Jarvis

检查流或文件的魔术字节:

https://stackoverflow.com/a/65667558/3225638

它使用纯 Java,但需要您定义要检测的类型的 enum


u
user1050755

如果您想要一种将文件扩展名映射到 mime 类型的可靠(即一致)方式,我使用的是:

https://github.com/jjYBdx4IL/misc/blob/master/text-utils/src/main/java/com/github/jjYBdx4IL/utils/text/MimeType.java

它包括一个捆绑的 mime 类型数据库,并且通过使用数据库初始化“程序化”条目,基本上颠倒了 javax.activation 的 MimetypesFileTypeMap 类的逻辑。这样,库定义的类型总是优先于非捆绑资源中可能定义的类型。


s
sahmad

我用下面的代码做到了。

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

        }
    }
}