如何从文件中准确确定哑剧数据?

2022-09-03 17:46:00

我正在向程序添加一些功能,以便我可以通过读取MIME数据来准确确定文件类型。我已经尝试了几种方法:

方法 1:

javax.activation.FileDataSource

FileDataSource ds = new FileDataSource("~\\Downloads\\777135_new.xls");  
String contentType = ds.getContentType();  
System.out.println("The MIME type of the file is: " + contentType);

//output = The MIME type of the file is: application/octet-stream

方法 2:

import net.sf.jmimemagic.*;

try
{
    RandomAccessFile f = new RandomAccessFile("~\\Downloads\\777135_new.xls", "r");
    byte[] fileBytes = new byte[(int)f.length()];
    f.read(fileBytes);
    MagicMatch match = Magic.getMagicMatch(fileBytes);
    System.out.println("The Mime type is: " + match.getMimeType());
}
catch(Exception e)
{
    System.out.println(e);
}

//output = The Mime type is: application/msword

方法 3:

import eu.medsea.mimeutil.*;

MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
File f = new File ("~\\Downloads\\777135_new.xls");
Collection<?> mimeTypes = MimeUtil.getMimeTypes(f);
String mimeType = MimeUtil.getFirstMimeType(mimeTypes.toString()).toString();
String subMimeType = MimeUtil.getSubType(mimeTypes.toString());
System.out.println("The Mime type is: " + mimeTypes + ", " + mimeType + ", " + subMimeType);

//output = The Mime type is: application/msword, application/msword, msword

我在 http://www.rgagnon.com/javadetails/java-0487.html 找到了这三种方法。然而,我的问题是,我正在测试这些方法的文件是我创建的一个文件,所以我知道它是一个Excel文件,但是除了第一种方法之外,所有三种方法仍然错误地将类型选择为msword,我相信这是因为该方法使用的内置FileTypeMap中的文件类型数量有限。

我环顾四周,有些人说这是因为在文件中检测偏移的方式,因此内容类型被错误地拾取,正如这个wiki中关于检测PHP中的文件类型所指出的那样。不幸的是,wiki继续使用扩展名来确定文件类型,这不是我想要做的,因为它不可靠。

任何人都可以为我指出正确的方向,以找到一种可以在Java中正确检测文件类型的方法吗?

干杯,阿列克谢·布鲁。

编辑:似乎没有具体的解决方案,正如@IronMensan在下面的评论中所说的那样。我确实发现了这篇非常有趣的研究论文,它以多种方式应用机器学习来帮助解决这个问题,但似乎没有一个完整的证据答案。我认为我在这里最好的选择是尝试将文件传递给excel文件阅读器并捕获任何不正确的格式异常。


答案 1

到目前为止,我发现的确定文件MIME类型的最准确的工具是Apache Tika。这是对我目前使用的(Tika版本1.0)的轻微修改。

import org.apache.tika.detect.DefaultDetector;
import org.apache.tika.detect.Detector;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.mime.MimeTypes;

private static final Detector DETECTOR = new DefaultDetector(
        MimeTypes.getDefaultMimeTypes());

public static String detectMimeType(final File file) throws IOException {
    TikaInputStream tikaIS = null;
    try {
        tikaIS = TikaInputStream.get(file);

        /*
         * You might not want to provide the file's name. If you provide an Excel
         * document with a .xls extension, it will get it correct right away; but
         * if you provide an Excel document with .doc extension, it will guess it
         * to be a Word document
         */
        final Metadata metadata = new Metadata();
        // metadata.set(Metadata.RESOURCE_NAME_KEY, file.getName());

        return DETECTOR.detect(tikaIS, metadata).toString();
    } finally {
        if (tikaIS != null) {
            tikaIS.close();
        }
    }
}

由于Tika将使用幻数,但在不确定时也会查看文件的内容,因此该过程可能有点耗时(我的PC检查15个文件需要3.268秒)。

另外,不要犯我一开始犯的错误。如果你得到 tika-core JAR,你还应该得到 tika-parsers JAR。如果你没有得到tika解析器,你不会得到任何例外,你根本无法准确地得到MIME类型,所以包含它是非常重要的。

另一种方法是获取tika-app JAR,其中包含tika-coretika-parsers和所有依赖项(它们很多:poi,poi-ooxml,xmlbeans,commons-compress,仅举几例)。


答案 2

正如评论中提到的,因为有太多可能的文件类型,它可能会被击中并错过所有可能的文件,但你可能知道你通常要处理的文件类型。这个出色的幻数列表帮助我最近对您提到的特定办公格式(搜索Microsoft Office)进行了检测,您将看到MS Office文件类型指定了子类型(在文件中进一步),并允许您具体计算出您拥有的文件类型。许多新格式,如ODT,DOCX,OOXML等使用ZIP文件来保存其数据,因此您可能需要先检测zip,然后查找详细信息。