前言

之前『利用 Magic Number 校验文件类型』一文讲解了校验文件类型的原理,并在文末给出了示例代码。但是每次去查询 Magic Number 对照表有点麻烦,于是我找了一些能够判断文件类型的三方库,可以省不少事儿。

作为开发者你应该知道,在 HTTP 协议消息头中,使用 Content-Type 来表示请求和响应中的媒体类型信息。这个字段在不同场景下有几种叫法,Content Type、Media Type、MIME Type,严格来说这几个名称并不完全对等,但在这里我们暂不做区分,大家明白意思就行。

Http Content-Type

它用来告诉服务端如何处理请求的数据,以及告诉客户端如何解析响应的数据。

因此我们可以通过这个值来判断文件类型。

URLConnection

URLConnection 是 JDK 自带的类,它相当于 URL 资源与应用程序之间的桥梁。它是一个抽象类,我们熟知的 HttpURLConnection 就是它的子类。

public class FileMatcher {

    private static final String MIME_TYPE_PNG = "image/png";
    
    public static boolean isPng(File file) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            String mime = URLConnection.guessContentTypeFromStream(fis);
            return MIME_TYPE_PNG.equals(mime);
        } catch (IOException e) {
            return false;
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

通过 guessContentTypeFromStream() 方法获取流的 Content Type,再与对应的 Media Type 比较即可。

另外 URLConnection 还有一个 guessContentTypeFromName() 方法,是根据文件名来判断,也就是之前说的通过文件扩展名,这种方法并不可靠,但是速度会快一些。

jMimeMagic

jMimeMagic 是一个用于确定文件或数据流的 MIME Type 的 Java 库。

添加依赖:

dependencies {
    implementation 'net.sf.jmimemagic:jmimemagic:0.1.5'
}

逻辑代码:

public class FileMatcher {

    private static final String MIME_TYPE_PNG = "image/png";
    
    public static boolean isPng(File file) {
        try {
            MagicMatch match = Magic.getMagicMatch(file, false);
            String mime = match.getMimeType();
            return MIME_TYPE_PNG.equals(mime);
        } catch (MagicParseException | MagicMatchNotFoundException | MagicException e) {
            return false;
        }
    }
}

可惜的是该库的上一次更新还停留在 2017 年。

Apache Tika Core

Apache Tika 工具包可检测和提取上千种不同文件类型中的元数据和文本。

依赖核心库即可:

dependencies {
    implementation 'org.apache.tika:tika-core:2.8.0'
}

逻辑代码:

public class FileMatcher {

    private static final String MIME_TYPE_PNG = "image/png";
    
    public static boolean isPng(File file) {
        Tika tika = new Tika();
        try {
            String mime = tika.detect(file);
            return MIME_TYPE_PNG.equals(mime);
        } catch (IOException e) {
            return false;
        }
    }
}

有 Apache 做背书,只能说稳得一匹。

后记

可以看到,这几种方法都是通过读取数据流的方式来校验文件类型,和之前『利用 Magic Number 校验文件类型』介绍的原理是一致的,同时由于这几种方法都能够处理数据流,所以并非只能传入 File,在网络传输数据流时也能够很方便地进行校验。