Как точно определить данные mime из файла?

Я добавляю некоторые функции в программу, чтобы я мог точно определить тип файлов, читая данные 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?

Cheers, Алексей Синий.

Изменить: похоже, нет конкретного решения для этого, как сказал @IronMensan в комментарии ниже. Я нашел этот действительно интересный исследовательский документ, который применяет машинное обучение несколькими способами, чтобы помочь решить проблему, но, похоже, нет полного доказательства ответ. Я считаю, что лучше всего попытаться передать файл в считыватель файлов excel и уловить любые неправильные исключения в формате.

Ответы

Ответ 1

Как упоминалось в комментариях, так как существует так много возможных типов файлов, он может быть поражен и пропущен для ВСЕХ возможных файлов, но вы, вероятно, знаете типы файлов, с которыми вы обычно сталкиваетесь. Этот отличный список магических чисел помог мне обнаружить недавно некоторые специфические офисные форматы (поиск в Microsoft Office), и вы увидите, что типы файлов MS Office имеют указанный подтип (который находится далее в файле) и позволяет вам конкретно определить, какой тип файла у вас есть. Многие новые форматы, такие как ODT, DOCX, OOXML и т.д., Используют ZIP файл для хранения своих данных, поэтому вам может понадобиться сначала обнаружить почтовый индекс, а затем искать специфику.

Ответ 2

До сих пор наиболее точный инструмент, который я нашел для определения типа файла 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 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 будет использовать магические числа, но также смотреть на содержимое файлов, когда вы не уверены, процесс может быть немного дорогостоящим (для моего ПК для просмотра 15 файлов потребовалось 3,268 секунды).

Кроме того, не делайте ту же ошибку, что и раньше. Если вы получаете JAR с тика-сердечником, вы также должны получить JAR для tika-parsers. Если вы не получите tika-parsers, вы не получите каких-либо исключений, вы просто не получите тип MIME точно, так что это действительно важно включить его.

Альтернативой является получение JAR-приложения tika-app, содержащего tika-core, tika-parsers и все зависимости (они много: poi, poi-ooxml, xmlbeans, commons-compress, просто чтобы назвать мало).

Ответ 3

Я не совсем уверен, насколько это точно, но это сработало для меня в простых случаях.

    FileNameMap fileNameMap = URLConnection.getFileNameMap();
    String type = fileNameMap.getContentTypeFor(filePath);