You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java后端实现.msg文件HTML预览的RTF转HTML问题求助

问题:.msg文件HTML预览的RTF解析困境

我在Java后端开发.msg文件的HTML预览功能,使用Apache POI读取.msg文件时,只能获取到纯文本或RTF格式的内容。RTF中虽然嵌入了HTML内容,但我无法将其完整解析为可用于网站邮件预览的标准HTML。目前采用了Stack Overflow上的正则替换方法,但无法解析全部HTML标签;尝试用Apache Tika解析RTF,结果返回空HTML。

现有正则解析实现

读取.msg文件的代码

InputStream inputStream = new FileInputStream(file);
MAPIMessage msgMessage = new MAPIMessage(inputStream);
return rtfToHtml(msgMessage.getRtfBody());

自定义RTF转HTML的正则方法

public static String rtfToHtml(String rtfText)
{
    StringBuilder sb = new StringBuilder();

    if (rtfText != null)
    {
        String[] lignes = rtfText.split("[\\r\\n]+");
        for (String ligne : lignes)
        {
            String tempLine = ligne.replaceAll("\\{\\\\*\\\\[m]?htmltag[\\d]*([^}]*)\\}", "$1")
                .replaceAll("\\\\htmlrtf0([^\\\\]*)\\\\htmlrtf", "$1")
                .replaceAll("\\\\htmlrtf \\{(.*)\\}\\\\htmlrtf0", "$1")
                .replaceAll("\\\\htmlrtf (.*)\\\\htmlrtf0", "")
                .replaceAll("\\\\htmlrtf[0]?", "")
                .replaceAll("\\\\field\\{\\\\*\\\\fldinst\\{[^}]*\\}\\}", "")
                .replaceAll("\\{\\\\fldrslt\\\\cf1\\\\ul([^}]*)\\}", "$1")
                .replaceAll("\\\\htmlbase", "")
                .replaceAll("\\\\*\\bkmkstart BM\\_", "")
                .replaceAll("\\\\par", "\n")
                .replaceAll("\\\\tab", "\t")
                .replaceAll("\\\\line", "\n")
                .replaceAll("\\\\page", "\n\n")
                .replaceAll("\\\\sect", "\n\n")
                .replaceAll("\\\\emdash", "ߞ")
                .replaceAll("\\\\endash", "ߝ")
                .replaceAll("\\\\emspace", "ߓ")
                .replaceAll("\\\\enspace", "ߒ")
                .replaceAll("\\\\qmspace", "ߕ")
                .replaceAll("\\\\bullet", "ߦ")
                .replaceAll("\\\\lquote", "ߢ")
                .replaceAll("\\\\rquote", "ߣ")
                .replaceAll("\\\\ldblquote", "&#201C;")
                .replaceAll("\\\\rdblquote", "&#201D;")
                .replaceAll("\\\\row", "\n")
                .replaceAll("\\\\cell", "|")
                .replaceAll("\\\\nestcell", "|")
                .replaceAll("([^\\\\])\\{", "$1")
                .replaceAll("([^\\\\])}", "$1")
                .replaceAll("[\\\\](\\{", "$1")
                .replaceAll("[\\\\](})", "$1")
                .replaceAll("\\\\u([0-9]{2,5})", "&#$1;")
                .replaceAll("\\\\'([0-9A-Fa-f]{2})", "&#x$1;")
                .replaceAll("\"cid:(.*)@.*\"", "\"$1\"")
                .replaceAll(" {2,}", " ")
                .replaceAll("\\\\htmlrtf[1]?(.*)\\\\htmlrtf0", "")
                .replaceAll("\\\\htmlrtf[01]?", "");

            if (!tempLine.replaceAll("\\s+", "").isEmpty())
            {
                sb.append(tempLine).append("\r\n");
            }
        }

        rtfText = sb.toString();

        int index = rtfText.indexOf("<html");
        if (index != -1)
        {
            return rtfText.substring(index);
        }
    }

    return null;
}

Apache Tika尝试的失败代码

public String convertUsingApacheTika(String rtfString) throws TikaException, IOException, SAXException
{
    Tika tika = new Tika();
    InputStream stream = TikaInputStream.get(rtfString.getBytes(StandardCharsets.UTF_8));
    BodyContentHandler handler = new BodyContentHandler(-1);
    Metadata metadata = new Metadata();

    AutoDetectParser parser = new AutoDetectParser();
    parser.parse(stream, handler, metadata);

    String htmlString = handler.toString();
    return htmlString;
}

解决建议

  • 使用Java自带的RTFEditorKit解析:不要用正则硬解析RTF,Java Swing包中的RTFEditorKit可以正确解析RTF结构并转为HTML,兼容性更好。示例代码:
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.rtf.RTFEditorKit;
import javax.swing.text.html.HTMLEditorKit;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.IOException;

public String rtfToHtml(String rtf) throws IOException, BadLocationException {
    // 处理Headless环境
    System.setProperty("java.awt.headless", "true");
    
    RTFEditorKit rtfKit = new RTFEditorKit();
    Document doc = rtfKit.createDefaultDocument();
    rtfKit.read(new StringReader(rtf), doc, 0);
    
    HTMLEditorKit htmlKit = new HTMLEditorKit();
    StringWriter writer = new StringWriter();
    htmlKit.write(writer, doc, 0, doc.getLength());
    
    return writer.toString();
}
  • 修复Apache Tika的用法:当前Tika代码有两个问题:一是没有指定文档类型导致AutoDetectParser识别错误,二是用了BodyContentHandler只会提取纯文本。改用RTFParser和ToHTMLContentHandler可以直接生成HTML:
import org.apache.tika.parser.rtf.RTFParser;
import org.apache.tika.sax.ToHTMLContentHandler;
import org.apache.tika.metadata.Metadata;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

public String rtfToHtmlWithTika(String rtfString) throws Exception {
    ToHTMLContentHandler htmlHandler = new ToHTMLContentHandler();
    Metadata metadata = new Metadata();
    // 明确指定内容类型为RTF
    metadata.set(Metadata.CONTENT_TYPE, "text/rtf");
    
    try (InputStream stream = new ByteArrayInputStream(rtfString.getBytes(StandardCharsets.UTF_8))) {
        new RTFParser().parse(stream, htmlHandler, metadata);
        return htmlHandler.toString();
    }
}
  • 直接提取.msg中的原始HTML:部分.msg文件会单独存储原始HTML内容,而非仅嵌入RTF。可以尝试调用MAPIMessage的getHtmlBody()方法获取(如果POI支持),或者通过MAPI属性ID0x1013直接读取HTML内容,这样能跳过RTF转HTML的步骤,直接拿到可用的HTML。

内容的提问来源于stack exchange,提问作者esh_08

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 03:25:57