【问题标题】:Obtain InputStream from XML element content从 XML 元素内容中获取 InputStream
【发布时间】:2012-03-13 02:17:41
【问题描述】:

我的 servlet 的 doPost() 收到一个 HttpServletRequest,它的 ServletInputStream 向我发送了一大块用 XML 包装的 uuencoded 数据。例如,有一个元素:

<filedata encoding="base64">largeChunkEncodedHere</filedata>

我需要解码块并将其写入文件。我想从块中获取一个 InputStream,使用 MimeUtility 将其解码为流,然后使用该流写入文件---我不想将这个大块读入内存。

XML 是扁平的;即,没有太多嵌套。我的第一个想法是使用 SAX 解析器,但我不知道如何切换到流以仅读取块。

感谢您的想法。

格伦

编辑 1: 请注意 JB Nizet 在 this post 中的悲观回答。

编辑 2: 我已经在下面肯定地回答了我自己的问题,并将下面的 maximdim 的答案标记为正确,即使它没有完全回答问题,它确实将我引导到 StAX API和伍德斯托克斯。

【问题讨论】:

  • 不清楚你想要完成什么。如果您正确使用 Streams,则不应将数据同时保存在内存中。另一方面,我认为在您的情况下并不重要,因为浏览器/客户端可能会在您有机会处理它之前将整个数据在 POST 中提交到您的 servlet,所以它应该与整个数据无关紧要已经在你身边。
  • @maximdim 问题是关于正确使用流。在这种情况下,使用两倍的内存会有所不同。

标签: java xml stream sax


【解决方案1】:

还有一个关于 Woodstox 的建议:它还可以从内部高效地解码 base64 编码的内容。为此,您需要将 XMLStreamReader 转换为 XMLStreamReader2(或 TypedXMLStreamReader),这是 Stax2 扩展 API 的一部分。

但有了它,您将获得自动处理 Base64 解码的方法 readElementAsBinary()getElementAsBinary()XMLStreamWriter2 同样具有用于写入二进制数据的 Base64 编码方法。

【讨论】:

    【解决方案2】:

    以下是有关在解析时如何从元素流式传输的一些详细信息 使用 Woodstox 框架可以使用 StAX。

    this article有一个很好的概述。

    从 XMLInputFactory 我们可以调用 createXMLStreamReader(java.io.InputStream 流)使用 ServletInputStream。这将返回一个 XMLStreamReader2,它 有一个 getText(Writer w, boolean preserveContents) 方法,该方法返回一个 int 写入的字节数。必须实施此方法。在里面 implementation Stax2ReaderImpl 有这个实现

    // // // StAX2, Pass-through text accessors
    public int getText(Writer w, boolean preserveContents)
        throws IOException, XMLStreamException
    {
        char[] cbuf = getTextCharacters();
        int start = getTextStart();
        int len = getTextLength();
    
        if (len > 0) {
            w.write(cbuf, start, len);
        }
        return len;
    }
    

    在这段代码中,我们需要更改 getTextCharacters() 方法,以便它 从 InputStream 中读取。在 Woodstox 测试中 TestGetSegmentedText testSegmentedGetCharacters() 方法我们看到一个 sr.getTextCharacters(offset, buf, start, len) 使用的方法。实际上是多个参数的javadoc XMLStreamReader.getTextCharacters() 显示了以下实现。

    int length = 1024;
    char[] myBuffer = new char[ length ];
    for ( int sourceStart = 0 ; ; sourceStart += length ) {
        int nCopied = stream.getTextCharacters( sourceStart, myBuffer, 0, length );
        if (nCopied < length) {
            break;
        }
    }
    

    【讨论】:

      【解决方案3】:

      您可以使用SAX filter 或XPath 来仅获取您感兴趣的元素。一旦您有了元素的内容,将其传递给MimeUtility.decode() 并将流写入文件。

      我建议您使用代码示例更新您的问题,并让我们知道什么不起作用。

      更新:

      这是使用 StaX2 解析器 (Woodstox) 的示例代码。出于某种原因,JDK 中包含的 StaX 解析器似乎没有可比较的 getText() 方法,至少快速浏览一下。

      显然输入 (r) 和输出 (w) 可以是任何 Reader/Writer 或 Stream - 此处仅使用 String 示例。

          Reader r = new StringReader("<foo><filedata encoding=\"base64\">largeChunkEncodedHere</filedata></foo>");
          Writer w = new StringWriter();
      
          XMLInputFactory2 xmlif = (XMLInputFactory2)XMLInputFactory2.newInstance();
          XMLStreamReader2 sr = (XMLStreamReader2)xmlif.createXMLStreamReader(r);
      
          boolean flag = false;
          while (sr.hasNext()) {
              sr.next();
              if (sr.getEventType() == XMLStreamConstants.START_ELEMENT) {
                  if ("filedata".equals(sr.getLocalName())) {
                      flag = true;
                  }
              }
              else if (sr.getEventType() == XMLStreamConstants.CHARACTERS) {
                  if (flag) {
                      sr.getText(w, false);
                      break;
                  }
              }
          }
          System.out.println(w);
      

      【讨论】:

      • 谢谢。我会看一下,但似乎“SAX 过滤器”不会返回流——这就是这个问题的重点。问题不是现有代码有什么问题(没有),而是应该写什么代码?
      • 好的,我已经为您发布了使用 StaX2 解析器的快速代码示例
      • 谢谢,虽然这并没有深入了解它是如何完成的,但它确实为我指明了正确的方向。我已经在自己的答案中加入了细节。
      猜你喜欢
      • 1970-01-01
      • 2017-06-14
      • 2018-08-06
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多