Java中无法解析此多部分MIME消息体

5

我不是在写邮件应用程序,因此我无法访问所有的标题等内容。我只有类似于这个问题末尾的块状物。我尝试使用JavaMail API解析它,使用类似于下面的代码:

Session s = Session.getDefaultInstance(new Properties());
InputStream is = new ByteArrayInputStream(<< String to parse >>);
MimeMessage message = new MimeMessage(s, is);
Multipart multipart = (Multipart) message.getContent();

但是,它只告诉我message.getContent是一个String类型的对象,而不是Multipart或MimeMultipart类型。此外,我并不需要整个JavaMail API的所有开销,我只需要将文本解析成它的各个部分。以下是一个示例:

这是一个MIME格式的多部分消息。\n\n------=_NextPart_000_005D_01CC73D5.3BA43FB0\nContent-Type: text/plain;\n\tcharset="iso-8859-1"\nContent-Transfer-Encoding: quoted-printable\n\n内容:\n\n            请在每周开始时阅读本内容。随时欢迎讨论。\n\n\n--=20\n\nMrs. Suzy M. Smith\n555-555-5555\nsuzy@suzy.com\n------=_NextPart_000_005D_01CC73D5.3BA43FB0\nContent-Type: text/html;\n\tcharset="iso-8859-1"\nContent-Transfer-Encoding: quoted-printable\n\n\n\n\n\n\n\n\n\n内容:\n           =20\n请在每周开始时阅读本内容。随时欢迎讨论。\n
--

Mrs. Suzy M. Smith
555-555-5555
suzy@suzy.com\n\n------=_NextPart_000_005D_01CC73D5.3BA43FB0--\n\n
4个回答

7

首先,我拿到了你的示例消息,并将所有的\n替换为换行符,\t替换为制表符。

然后,我从Mime4J项目中下载了JAR文件,这是Apache James的一个子项目,并使用上述转换后的消息作为输入,执行了GUI解析示例org.apache.james.mime4j.samples.tree.MessageTree。显然,Mime4J能够解析该消息并提取HTML消息部分。


我下载了那些JAR文件并尝试了一下。 我创建了一个ContentHandler类并使用MimeStreamParser.parse。在我的处理程序中,会调用startMessage,然后调用startHeader和endHeader,这很好,因为没有标头。 然后,调用body,然后调用endMessage。 我期望在其中获得一些start和end bodyParts。 或者,由于有2个部分,body会被调用两次? - Bynan
我按照DwB上面建议的方法添加了一些头信息,现在MimeStreamParser.parse运行得非常好。感谢提供的信息。我不能+1你,因为我的声望不够... - Bynan

6

您发布的文本存在一些问题。

它不是有效的多部分mime。请参考wikipedia reference,虽然不是规范的,但仍然正确。

mime边界未定义。从维基百科的例子可以看出:Content-Type: multipart/mixed; boundary="frontier"显示边界为“frontier”。在您的示例中,“----=_NextPart_000_005D_01CC73D5.3BA43FB0”是边界,但这只能通过扫描文本(即mime格式不正确)来确定。您需要指示传递mime内容的人也需要知道mime边界值,该值未在消息头中定义。如果获取消息的整个正文,则将拥有足够的信息,因为消息正文以MIME-Version: 1.0开头,后跟Content-Type: multipart/mixed; boundary="frontier",其中frontier将被替换为编码mime的边界值。

如果发送主体的人是一个“傻瓜”(从猴子改为傻瓜,因为猴子太有判断力了——我的错),并且不会(更可能是不知道如何)发送完整的主体,您可以通过扫描文本以查找以“--”开头和结尾的行(即--boundary--)来推导出边界。请注意,我提到了“行”。终端边界实际上是“--boundary--\n”。
最后,您发布的内容有两个部分。第一部分似乎定义了要在第二部分中进行的替换。如果是这样,第一部分的Content-Type应该是除“text/plain”之外的其他内容类型。也许是“companyname/substitution-definition”或类似的东西。这将允许多个(例如未来增强)替换格式。

虽然很有启发性,但我实际上无法对此做任何事情。文本以这种方式呈现给我。我需要一种解析它的方法,如果没有现有的工具可以做到,我想我只能手动完成... - Bynan
通过按照您建议的方式添加一些标题信息,vanje在下面建议的解析器运行得非常好。感谢您提供的信息。我无法+1您,因为我的声望不够... - Bynan

4
可以从HTTP请求中创建MimeMultipart。
javax.mail.internet.MimeMultipart m = new MimeMultipart(new ServletMultipartDataSource(httpRequest));

public class ServletMultipartDataSource implements DataSource {
    String contentType;
    InputStream inputStream;
    public ServletMultipartDataSource(ServletRequest request) throws IOException {
        inputStream = new SequenceInputStream(new ByteArrayInputStream("\n".getBytes()), request.getInputStream());
        contentType = request.getContentType();
    }
    public InputStream getInputStream() throws IOException {
        return inputStream;
    }
    public OutputStream getOutputStream() throws IOException {
        return null;
    }
    public String getContentType() {
        return contentType;
    }
    public String getName() {
        return "ServletMultipartDataSource";
    }
}

要获取提交的表单参数需要解析BodyPart头:

public String getStringParameter(String name) throws MessagingException, IOException {
    for (int i = 0; i < getCount(); i++) {
        BodyPart bodyPart = m.getBodyPart(i);
        String[] nameHeader = bodyPart.getHeader("Content-Disposition");
        if (nameHeader != null && content instanceof String) {
            for (String bodyName : nameHeader) {
                if (bodyName.contains("name=\"" + name + "\"")) return String.valueOf(bodyPart.getContent());
            }
        }
    }
    return null;
}

2
如果您使用javax.servlet.http.HttpServlet接收消息,那么您需要使用HttpServletRequests.getHeaders获取HTTP标头content-type的值。然后,您将使用org.apache.james.mime4j.stream.MimeConfig.setHeadlessParsing将MimeConfig与信息一起设置,以便它可以正确处理mime消息。
看起来您正在使用HttpServletRequest.getInputStream读取请求内容。返回的输入流仅具有HTTP标头之后消息的内容(以空行终止)。这就是为什么您必须从HTTP标头中提取content-type并使用setHeadlessParsing将其提供给解析器的原因。

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