如何获取 .MSG 文件的 MIME 类型?

22

我已经尝试了找到文件 MIME 类型的这些方法...

Path source = Paths
                .get("C://Users/akash/Desktop/FW Internal release of MSTClient-Server5.02.04_24.msg");
        System.out.println(Files.probeContentType(source));

上面的代码返回 null...
如果我使用 Apache 的 TIKA API 获取 MIME 类型,那么它会将其作为 text/plain 给出...

但是我希望结果为 application/vnd.ms-outlook

更新

我还使用了以下代码来使用 MIME-Util.jar...

MimeUtil2 mimeUtil = new MimeUtil2();
        mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
        RandomAccessFile file1 = new RandomAccessFile(
                "C://Users/akash/Desktop/FW Internal release of MSTClient-Server5.02.04_24.msg",
                "r");
        System.out.println(file1.length());
        byte[] file = new byte[624128];
        file1.read(file, 0, 624128);
        String mimeType = MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(file)).toString();

这给我输出结果为application/msword

更新:

Tika API 超出了项目范围,因为它太大了...

那么我该如何找到 MIME 类型呢?


你可以使用魔数来检查文件并返回MIME类型application/vnd.ms-outlook。对于.msg文件:D0 CF 11 E0 A1 B1 1A E1 - Duffydake
请问您能否给我提供一个链接参考,说明您从哪里得到了这个特定的魔数...因为它存在于每个使用CFB配置打包字节的文件中... - CoderNeji
我在这里找到了它(https://billatnapier.wordpress.com/2013/04/22/magic-numbers-in-files/),但你是对的,这似乎不正确。 - Duffydake
它是使用Outlook创建的。 - CoderNeji
Apache POI 是一个很好的包含选项。 - CoderNeji
显示剩余2条评论
4个回答

10

我尝试了一些可能的方法,使用tika可以得到你期望的结果,但我没有看到你使用的代码,所以无法进行二次检查。

我尝试了不同的方法,但并非所有都在代码片段中:

  1. Java 7 Files.probeContentType(path)
  2. URLConnection从文件名和内容类型猜测中检测mime
  3. JDK 6 JAF API javax.activation.MimetypesFileTypeMap
  4. MimeUtil与我找到的所有MimeDetector子类
  5. Apache Tika
  6. Apache POI scratchpad

这是测试类:

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URLConnection;
import java.util.Collection;

import javax.activation.MimetypesFileTypeMap;

import org.apache.tika.detect.Detector;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.mime.MediaType;
import org.apache.tika.parser.AutoDetectParser;

import eu.medsea.mimeutil.MimeUtil;

public class FindMime {

    public static void main(String[] args) {
        File file = new File("C:\\Users\\qwerty\\Desktop\\test.msg");

        System.out.println("urlConnectionGuess " + urlConnectionGuess(file));

        System.out.println("fileContentGuess " + fileContentGuess(file));

        MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();

        System.out.println("mimeTypesMap.getContentType " + mimeTypesMap.getContentType(file));

        System.out.println("mimeutils " + mimeutils(file));

        System.out.println("tika " + tika(file));

    }

    private static String mimeutils(File file) {
        try {
            MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
            MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.ExtensionMimeDetector");
//          MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.OpendesktopMimeDetector");
            MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.WindowsRegistryMimeDetector");
//          MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.TextMimeDetector");
            InputStream is = new BufferedInputStream(new FileInputStream(file));
            Collection<?> mimeTypes = MimeUtil.getMimeTypes(is);
            return mimeTypes.toString();
        } catch (Exception e) {
            // TODO: handle exception
        }
        return null;
    }

    private static String tika(File file) {
        try {
            InputStream is = new BufferedInputStream(new FileInputStream(file));
            AutoDetectParser parser = new AutoDetectParser();
            Detector detector = parser.getDetector();
            Metadata md = new Metadata();
            md.add(Metadata.RESOURCE_NAME_KEY, "test.msg");
            MediaType mediaType = detector.detect(is, md);
            return mediaType.toString();
        } catch (Exception e) {
            // TODO: handle exception
        }
        return null;
    }

    private static String urlConnectionGuess(File file) {
        String mimeType = URLConnection.guessContentTypeFromName(file.getName());
        return mimeType;
    }

    private static String fileContentGuess(File file) {
        try {
            InputStream is = new BufferedInputStream(new FileInputStream(file));
            return URLConnection.guessContentTypeFromStream(is);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}

这是输出结果:

urlConnectionGuess null
fileContentGuess null
mimeTypesMap.getContentType application/octet-stream
mimeutils application/msword,application/x-hwp
tika application/vnd.ms-outlook

更新:我添加了这个方法来测试使用Tika的其他方式:

private static void tikaMore(File file) {
    Tika defaultTika = new Tika();
    Tika mimeTika = new Tika(new MimeTypes());
    Tika typeTika = new Tika(new TypeDetector());
    try {
        System.out.println(defaultTika.detect(file));
        System.out.println(mimeTika.detect(file));
        System.out.println(typeTika.detect(file));
    } catch (Exception e) {
        // TODO: handle exception
    }
}

测试了一个没有扩展名的msg文件:

application/vnd.ms-outlook
application/octet-stream
application/octet-stream

测试过将一个txt文件重命名为msg:

text/plain
text/plain
application/octet-stream

在这种情况下,似乎使用空构造函数是最简单和最可靠的方法。

更新,你可以使用Apache POI scratchpad来创建自己的检查器,例如这是一个获取消息mime或如果文件不是正确格式(通常为org.apache.poi.poifs.filesystem.NotOLE2FileException: Invalid header signature)则返回null的简单实现:

import org.apache.poi.hsmf.MAPIMessage;

public class PoiMsgMime {

    public String getMessageMime(String fileName) {
        try {
            new MAPIMessage(fileName);
            return "application/vnd.ms-outlook";
        } catch (Exception e) {
            return null;
        }
    }
}

它没有给我想要的解决方案...即使我拿一个文本文件并将其扩展名更改为.msg,然后使用该文件获取MIME类型,它仍然会输出tika application/vnd.ms-outlook...不过还是感谢你的工作... - CoderNeji
您更新后的代码仍然存在相同的问题...很抱歉...请按照以下步骤操作:1.创建一个文本文件。2.保存它。3.将文件扩展名改为.msg 4.使用此文件运行程序...您将获得应用程序/vnd.ms-outlook的输出。 - CoderNeji
这正是我使用 tikaMore 方法所做的,结果是 text/plain。请尝试使用上述方法。 - Paizo
请检查一下我使用Apache POI Scratchpad的最新更新。 - Paizo
当我尝试使用tika-core 1.14时,它给了我application/x-tika-msoffice而不是application/vnd.ms-outlook。 - maya16
显示剩余2条评论

4
从@Duffydake的评论中得到启示,我尝试读取魔数。同意MS文件头的前8个字节保持不变,为D0 CF 11 E0 A1 B1 1A E1(有趣的是前四个字节看起来像DoCFilE),但您可以查看此link以了解完整的头部并找到文件类型。(例如,在链接中找到一个Excel文件,但您可以使用类似的字节读取方法来查找msg文件类型)
如果您可以假设没有人会玩弄和存储.doc或.xls文件作为.msg文件,那么您只需要读取头部的前8个字节并将其与文件扩展名组合即可。例如:if(fileExtension.equals(".msg")&&hexHeaderString.equals('D0 CF 11 E0 A1 B1 1A E1'){mimeType=="application/vnd.ms-outlook"}

实际上我的应用程序是为客户而设计的,我不能假设任何东西......我已经尝试了读取8个字节的头文件....抱歉.... - CoderNeji
那么就不要读取8个字节,按照链接中提到的读取更多字节。链接清楚地解释了如何从文件头中确定Excel文件。您可以尝试类似的头部读取来查找.msg文件。您检查了我粘贴的链接吗? - Optional

2
您可以尝试将文件转换为byte[],然后使用MimeMagicMaven位置在这里)来处理它。类似这样的代码:
byte[] data = FileUtils.toByteArray("file.msg");
MagicMatch match = Magic.getMagicMatch(data);
String mimeType = match.getMimeType();

我不确定这个方法是否能够百分之百地成功,但是尝试一下并不会死亡 :)


0

我必须得到另一个解决方法。我发现 MS 文档(doc、docx、xls、xlsx、msg)是带有不同扩展名的压缩文件。因为它超出了当前范围,所以我没有测试每种 MS 文件类型。

简单地展开文件并:

对于 Docx:打开 [Content_Types].xml 并检查是否包含“wordprocessingml”

对于 XlsX:打开 [Content_Types].xml 并检查是否包含“spreadsheetml”

对于 doc:检查文件“WordDocument”

对于 xls:检查文件“Workbook”

对于 msg:检查文件“__properties_version1.0”

我仍在测试 msg 是否存在更好的使用方法,但此文件存在于已发送和未发送的消息中,因此我假设可以安全使用。


我正在使用.Net进行工作,所以我不确定如何在Java中实现。在我的情况下,我们使用7zip应用程序来扩展文件。您可以(我假设)在您的环境中使用内置的压缩/解压缩模块。请查看此帖子。https://dev59.com/Lmox5IYBdhLWcg3wQSIO - Atron Seige

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接