web-dev-qa-db-ja.com

ファイルから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

これらの3つのメソッドは http://www.rgagnon.com/javadetails/Java-0487.html で見つかりました。しかし、私の問題は、これらのメソッドをテストしているファイルが私が作成したものであり、それがExcelファイルであることを知っていることですが、それでも3つのメソッドすべてが、最初のメソッドが原因であると信じている最初のメソッドを除いて、タイプをmswordとして誤って取得していますメソッドが使用する組み込みのFileTypeMap内の限られた数のファイルタイプ。

私は周りを見回しましたが、これはファイルでオフセットが検出される方法が原因であると言う人がいます。これは、ファイルタイプの検出について wiki で指摘されているように、コンテンツタイプが正しく取得されないためです。 PHPで。残念ながら、ウィキは拡張子を使用してファイルタイプを決定しますが、信頼性が低いため、私がやりたいことではありません。

Javaお願いします)内でファイルタイプを正しく検出するメソッドを正しい方向に向けることができますか?

乾杯、アレクセイブルー。

編集:@IronMensanが以下のコメントで述べたように、これに対する特定の解決策はないようです。私はこれが本当に面白いと思いました 研究論文 問題を助けるためにいくつかの方法で機械学習を適用しますが、完全な証明の答えはないようです。ここでの最善の策は、ファイルをExcelファイルリーダーに渡して、誤った形式の例外をキャッチすることだと思います。

14
Alexei Blue

コメントで述べたように、考えられるすべてのファイルでヒットしたり見逃したりする可能性のあるファイルタイプが非常に多いためですが、通常処理するファイルのタイプはおそらくご存知でしょう。この マジックナンバーの優れたリスト 最近、あなたが言及した特定のOffice形式(Microsoft Officeを検索)の周りを検出するのに役立ちました。MSOfficeファイルタイプにはサブタイプが指定されていることがわかります(これはファイルのさらに奥にあります)、どのタイプのファイルを持っているかを具体的に把握できます。 ODT、DOCX、OOXMLなどの多くの新しい形式は、Zipファイルを使用してデータを保持するため、最初にZipを検出してから、詳細を探す必要がある場合があります。

3
Paul Jowett

これまでのところ、ファイルの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-parsersを取得しない場合、例外は発生しません。MIMEタイプを正確に取得できないため、これを含めることが非常に重要です。

別の方法は、tika-app JARを取得することです。これには、tika-coretika-parsersおよびすべての依存関係が含まれます(これらはロット:poi、poi-ooxml、xmlbeans、commons-compress、ほんの数例)。

28
rodrigo.garcia

それがどれほど正確かは完全にはわかりませんが、これは単純なケースではうまくいきました。

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