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", "ÉC;") .replaceAll("\\\\rdblquote", "ÉD;") .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
相关产品推荐
相关产品推荐

