MimeBodyPart getContent 损坏二进制数据

Posted

技术标签:

【中文标题】MimeBodyPart getContent 损坏二进制数据【英文标题】:MimeBodyPart getContent corrupts binary data 【发布时间】:2014-05-26 06:11:21 【问题描述】:

我使用 javax.mail.internet.MimeBody* 1.4.1 版

我的程序想要使用 MimeMultiPart 从服务器向客户端发送一些具有多层嵌套的二进制数据。我观察到,如果我们使用 GetContent,它会破坏数据。我能够用这个 sn-p 重现这个问题

  public static void CreateResponse() throws Exception 
        //Simulate the Server side
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        MimeMultipart multiPartValues = new MimeMultipart();
        MimeBodyPart valueBody = new MimeBodyPart();

        byte[] firstKeyValue = new byte[]  (byte)0x8c;
        valueBody.setContent(firstKeyValue,"application/octet-stream");
        valueBody.addHeader(RestMessageHeaders.CONTENT_LENGTH,
                Integer.toString(firstKeyValue.length));
        multiPartValues.addBodyPart(valueBody);

        Object input = valueBody.getContent();
        System.out.println(String.format("input %02X", ((byte[])input)[0]));
        multiPartValues.writeTo(outputStream);

        //Simulate the client side
        byte[] mimeOutput = outputStream.toByteArray();

        ByteArrayDataSource ds = new ByteArrayDataSource(mimeOutput,
                "multipart/mixed");
        MimeMultipart mp = new MimeMultipart(ds);
        MimeBodyPart part = (MimeBodyPart) mp.getBodyPart(0);

        byte[] myOutput = new byte[1];
        //Verified that getContent returns a String, why ?? 
        Object output = part.getContent();
        System.out.println("getContent type " + output.getClass());
        String result = (String)output;
        ByteArrayDataSource partDS = new ByteArrayDataSource(result, "multipart/mixed");
        partDS.getInputStream().read(myOutput); 
        System.out.println(String.format("getContent %02X %02X", result.getBytes()[0],result.getBytes()[1]));
        System.out.println(String.format("getContent %02X", myOutput[0]));

        part.getInputStream().read(myOutput);
        System.out.println(String.format("getInputStream %02X", myOutput[0]));

        part.getRawInputStream().read(myOutput);
        System.out.println(String.format("getRawInputStream %02X", myOutput[0]));


这是输出

        input 8C
        getContent type class java.lang.String
        getContent C2 8C
        getContent C2
        getInputStream 8C
        getRawInputStream 8C

我已经完全简化了这里的代码,使用 get(Raw)InputStream 看起来很明显,但是我们有嵌套的MultiPart,并且顶层正在执行 getContent,这导致它在某些情况下失败。

    输入是字节数组类型,但在客户端 getContent 以字符串响应。服务器将内容设置为 application/octet-stream 但在客户端它以字符串形式输出。这里出了什么问题? 我不确定为什么在 8c 之前添加了字节 c2。 8c 字符有什么特别之处? getInputStream 和getRawInputStream 有什么区别。什么时候使用一个而不是另一个?

【问题讨论】:

【参考方案1】:

服务器正在创建和客户端正在读取的完整流包含什么?

请注意,如果在没有 MimeMessage 的情况下使用 MimeMultipart,您会错过一些由 MimeMessage 自动为您完成的事情,尤其是您会错过对 MimeMultipart.updateHeaders() 的调用。由于该方法受保护,因此您需要继承 MimeMultipart 并在调用 writeTo 之前调用该方法。如果这不能解决您的问题,请向我们展示在流中写入和读取的确切数据。

如上所述,如果您需要二进制数据,您几乎可以肯定要使用 getInputStream。 getRawInputStream 在解码之前为您提供数据,例如,base64 输入而不是二进制输出。

【讨论】:

我们有一个键值服务器(Project Voldemort),我们使用 MimeMultiPart 将消息从服​​务器传输到客户端。 Key 和 Value 是字节数组。我们使用 MimeMultiPart 来处理序列化/反序列化。使用 MimeMessage 可以修复它吗?也不确定最后一行。你的意思是 getInputStream 为 base64 和 getRawInputStream 为 byteArray ? 这可能比我在评论中解释的要长... JavaMail 的某些部分是“懒惰的”。 MimeMessage 对您隐藏了其中的一些内容。如果您不使用 MimeMessage,则必须自己处理这些方面。阅读 updateHeaders 方法的 javadocs,如果你不明白,请告诉我。 getInputStream 方法返回解码后的数据。它与字节数组无关。 getRawInputStream 返回编码数据。它与字节数组无关。 setContent 方法可能接受一个字节数组,但 getContent 方法将返回一个适合 MIME 类型的对象。如果 MIME 类型没有经过特殊处理(例如文本类型),getContent 将只返回一个 InputStream,您必须根据需要处理字节。 谢谢比尔。我有最后一个问题,如果你想发送一些字节数组,那么我想的答案是使用 1)将所有内容包装在 MimeMessage 中,因为 MimeMultiPart 可能无法正确序列化/反序列化 2)使用 getInputStream 而不是 getRawInputStream (他们都应该理论上字节数组是一样的)。 那里仍然有些混乱。如果您可以使用 MimeMessage,那就更容易了。如果不能,则需要创建自己的 MimeMultipart 子类,以便在调用 writeTo 方法之前调用 updateHeaders 方法。一旦你这样做了,MimeMultipart 将正确地序列化。一旦数据被序列化,任何关于它是否来自字节数组的知识都会丢失。在反序列化时,它只是一个字节流,如果需要,可以将其读入字节数组。【参考方案2】:

如果 getContent() 返回一个字符串并且你想要二进制,不要使用它。字符串不是二进制数据的容器。使用 getInputStream() 并复制字节。

【讨论】:

我希望它像你说的那样简单。获取内容返回对象。序列化前是字节数组,反序列化后返回字符串。 我不明白这条评论。使用 getInputStream() 有什么不简单的? GetContent 返回一个对象。在序列化之前,对象是字节数组。但是在序列化/反序列化之后,对象是字符串。所以 GetContent 并不总是错误的。也不确定 GetInputStream 和 GetRawInputStream 之间的区别。

以上是关于MimeBodyPart getContent 损坏二进制数据的主要内容,如果未能解决你的问题,请参考以下文章

从 MimeBodyPart javax.mail.message 中删除两个连字符

Java mail发送邮件附件出现.eml文件夹

资损业务产品分析资损防控规范

Symfony $request->getContent() 格式错误?

PHP curl_multi_getcontent函数

支付系统资损分析