2012-02-22 121 views
1

我的servlet的doPost()接收到一个HttpServletRequest,它的ServletInputStream向我发送了大量用XML封装的uuencoded数据。例如,有一个元素:从XML元素内容中获取InputStream

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

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

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

感谢您的想法。

格伦

编辑1:注JB Nizet在this post悲观的答案。

编辑2:我肯定低于回答我自己的问题,并标注以下为正确maximdim的答案,尽管它并不完全回答这个问题,它没有直接我到StAX API和Woodstox。

+0

不清楚你想完成什么。如果您正确使用Streams,则不应将数据同时保存在内存中。另一方面,我认为你的情况并不重要,因为浏览器/客户端可能会在POST中将整个数据提交给你的servlet,然后你才有机会处理它,所以它应该不会有太大影响,因为整个数据将会已经在你身边的记忆中。 – maximdim 2012-02-22 17:54:25

+0

@maximdim问题是关于正确使用流。使用两倍的内存在这种情况下有所不同。 – Glenn 2012-02-22 19:41:58

回答

0

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

我建议你用代码示例更新你的问题,让我们知道什么是行不通的。

更新:

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

显然,输入(r)和输出(w)可以是任何读写器或流 - 使用字符串就在这里举个例子。

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); 
+0

谢谢。我会看看,但似乎“SAX过滤器”不返回流 - 这是这个问题的关键。问题不是现有代码有什么问题(没有),但应该写什么代码? – Glenn 2012-02-22 21:53:21

+0

好吧,我已经为您使用StaX2解析器发布了快速代码示例 – maximdim 2012-02-23 13:54:31

+0

谢谢,虽然这并没有涉及到它如何完成的基本原理,但它确实指向了我正确的方向。我已将细节放入自己的答案中。 – Glenn 2012-02-24 04:43:02

0

下面是关于如何从一个元素流在解析 使用StAX是可能的,使用Woodstox框架的一些细节。

this article有一个很好的概述。

从XMLInputFactory我们可以使用ServletInputStream调用createXMLStreamReader(java.io.InputStream stream)。这将返回一个XMLStreamReader2,其中 有一个getText(Writer w,boolean preserveContents)方法,该方法返回int的 写入的字节数。该方法必须执行。在 实施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)方法的使用。实际上,多参数XMLStreamReader.getTextCharacters()的javadoc显示了以下实现。

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; 
    } 
} 
0

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

但是,通过这种方法,您可以获得自动处理Base64解码的方法readElementAsBinary()getElementAsBinary()XMLStreamWriter2类似地具有用于写入二进制数据的Base64编码方法。