【问题标题】:Byte length difference when retrieving from WebSphere MQ Message从 WebSphere MQ 消息检索时的字节长度差异
【发布时间】:2014-09-26 00:19:13
【问题描述】:

在 Java 中,我正在轮询一个 WebSphere MQ 消息队列,期待一条完全由 XML 组成的 `STRING 格式的消息。此 XML 的一部分将包含文件附件的字节(任何格式:pdf、图像等),然后将其转换为 blob 以存储在 Oracle Db 中并稍后检索。

我遇到的问题是,发送的示例文件的已知大小最终在我的 Db 中具有不同的大小。我没有向字节添加任何内容(据我所知),并且在收到消息后大小似乎直接变大了。我无法确定我是否以某种方式在检索时添加信息,从bytes 转换 -> String,或者当发件人填充消息时这是否发生在前端。

我在检索消息时的代码:

              inboundmsg = new MQMessage();
              inboundmsg = getMQMsg(FrontIncomingQueue, gmo);
              strLen = inboundmsg.getMessageLength();
              strData = new byte[strLen];
              ibm_id = inboundmsg.messageId;
              inboundmsg.readFully(strData);
              inboundmsgContents = new String(strData);

我看到一个已知大小为 21K 的文件变为 28K。一位同事建议字符集/编码可能是问题所在。我没有在上面对String 的构造函数调用中指定字符集,也没有在从字符串转换回来时对getBytes 的任何调用中指定字符集(用于其他不相关的用途)。我的默认字符集是 ISO-8859-1。在与发起消息传输的供应商交谈时,我问她使用的是什么字符集。她的回复:

“我在 C# 中使用 File.WriteAllBytes 方法 - 我将文件的路径传递给它,并将其写入一个字节 []。我在 MSDN 上找不到任何关于该函数使用什么编码的文档。方法创建一个字节数组,从我今天早上在线阅读的内容来看,没有编码,它只是一个没有编码的 8 位无符号二进制数据序列。”

另一位同事认为 MQ 字符集可能是罪魁祸首,但我对文档的阅读表明 MQ 字符集仅影响 readStringreadLinewriteString 的行为。

如果我完全绕过 MQ,并使用文件输入流和本地文件填充字节数组,则文件大小一直保留到 Db 存储,因此这肯定会在消息传输时或期间发生。

【问题讨论】:

  • vendor 有没有说什么 API 用于发送消息?它是“xms”.net 客户端还是 .net 类?想知道消息上是否有 RFH2 标头。标头可能是额外的大小,但 4k 有点大。 (JMS 可以读取 RFH2)。您是否使用 rfhutil 或 amqsbcg 浏览了队列中的消息以查看其中的内容?你得到的字符串到底是什么?
  • 我向供应商发送了一封电子邮件,询问有关 API 的问题。在将字节数组 StrData 转换为上一行中的字符串后,我直接记录了包含消息内容的字符串。这是巨大的,但这里是一个摘要:
  • w3.org/2001/XMLSchema-instance" xmlns:xsd="w3.org/2001/XMLSchema"> 1SNDNSP 09/25/2014 15:11:18lp.PNG20Plants AttachmentDescription> iVBORw0KGgoAAAAANS … AAAAAASUVORK5CYII=
  • 对不起,我意识到上面的评论不是很可读。长话短说,来自 PdfBytes 标签之间的字符串导致文件大小大于通过 MQ 传输之前的原始文件大小。我在那里放了一个'...'来表示一个非常大的字符串。它似乎只是似乎具有不同大小的字节字符串;消息的其余部分很好,并且很容易解析。字符串在解析之前大小错误,我已经验证了。

标签: java encoding character-encoding byte ibm-mq


【解决方案1】:

问题的措辞很明显。您描述了一个包含任意二进制数据并尝试将其作为字符串处理的有效负载。这两件事是相互排斥的。

由于供应商未提供有效的 XML,这似乎很复杂。例如,考虑附件:

   <PdfBytes>iVBORw0KGgoAAAANS … AAAAASUVORK5CYII=</PdfBytes>

如果附件合法地包含任何 XML 特殊字符,例如 &lt;&gt;,则结果是无效的 XML。如果它包含空字节,一些解析器会假设它们已经到达文本的末尾并停止解析。这就是为什么您通常会看到 XML 中的任何附件要么转换为 Base64 以进行传输,要么转换为十六进制。

供应商描述了编写原始二进制数据,这表明您收到的内容包含非字符串字符,因此应该作为字符串数据发送。如果她描述了某种使附件符合 XML 的转换,那么字符串将是合适的。

有趣的是,Base64 编码产生的有效负载比原始编码大 1.33 倍。巧合的是21k * 1.3 = 28k?有人会认为接收到的实际上是 Base64 格式的二进制有效载荷。这实际上 可以解析为字符串并解释文件大小的差异。但这根本不是供应商所描述的。她说她正在编写“没有编码的 8 位无符号二进制数据”而不是 Base64。

所以我们预计它会失败,但不一定会导致更大的有效负载。考虑接收String 格式的消息的WebSphere MQ 将尝试转换它。如果消息的 CCSID 与 GET 上请求的不同,则 MQ 将尝试转换。如果入站 CCSID 是 UTF-16 或任何双字节字符集,则某些字符将从 1 个字节扩展为 2 个字节 - 假设转换不会遇到导致其失败的无效二进制字符。

如果两个 CCSID 相同,则不会在 MQ 类中尝试转换,但仍然存在一个问题,即 something 必须解析根据定义无效并因此受到约束的 XML 有效负载到意想不到的结果。如果二进制有效负载不包含任何 XML 特殊字符,并且解析器没有阻塞任何嵌入的空字节,那么解析器将竭尽全力原谅不兼容的有效负载。如果它到达&lt;/PdfBytes&gt; 标签而没有阻塞,它可能会假设有效负载是有效的,并转换&lt;PdfBytes&gt;...&lt;/PdfBytes&gt; 标签之间的所有内容。大概是Base64。

当然,所有这些都是猜测。但是在负载明确不是字符串数据的情况下,任何将其解析为字符串数据的尝试都将彻底失败或产生意想不到的和潜在的奇怪结果。实际上你很不幸,它没有彻底失败,因为现在人们期望问题出在你身上,而这显然是供应商的错。

假设负载的内容保持不变,供应商应该发送bytes 消息,而您应该以bytes 接收它们。这至少可以解决 MQ 在协调预期格式与实际接收格式时遇到的问题,但它仍然是无效的 XML。如果供应商在消息集中发送二进制数据以键入String,而您将其处理为bytes,那么算上您的祝福并以这种方式使用它,但不要指望它是可靠的。最终,您将获得一个带有嵌入式 XML 特殊字符的有效负载,然后您将度过非常糟糕的一天。

理想情况下,供应商应该知道在 XML 有效负载中发送二进制数据而不首先将其转换为字符串的情况更好,并且由他们来修复它,以使其符合 XML 规范并且可靠。

请参阅此 MSDN 页面:XML, SOAP, and Binary Data

【讨论】:

  • 感谢 T. Rob 提供的信息。这个问题确实最终导致了 base64 转换。供应商现在提供原始附件字节的 base64 字符串表示,在解析 XML 后,我的应用程序调用 DatatypeConverter.parseBase64Binary(b64string) 以获取原始字节以供 Db 存储和以后检索。
  • 很高兴听到它有帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-10
  • 2021-08-17
  • 1970-01-01
  • 1970-01-01
  • 2011-03-28
  • 2010-09-21
相关资源
最近更新 更多