简体   繁体   English

Base64 值导致 SAX 解析器中的 StringBuilder.append 内存不足错误

[英]Base64 value causing StringBuilder.append out of memory error in SAX parser

I am using SAX parser to parse the XML file which is the response from client's server.我正在使用 SAX 解析器来解析 XML 文件,该文件是来自客户端服务器的响应。 tag has inside the Base64 decoded value of the files (jpg, png, pdf etc.).标签包含在文件(jpg、png、pdf 等)的 Base64 解码值中。 Some of them I can easily write to file but with some of them (I am guessing the large one) I have a problem during StringBuilder.append on the overwritten method of DefaultHandler:其中一些我可以轻松地写入文件,但其中一些(我猜是大的)我在 StringBuilder.append 期间在 DefaultHandler 的覆盖方法上遇到了问题:

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
    if (valueActive) { // set to true only when <value> tag is active
        stringBuilder.append(ch, start, length); // OUT OF MEMORY error
    }
}

On the next steps, I am using Base64Encoder to write files:在接下来的步骤中,我使用 Base64Encoder 编写文件:

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {

    if (qName.equalsIgnoreCase("value")) {

        valueActive = false;

        try (OutputStream stream = new FileOutputStream(my file here)) {

        base64 = Base64.getDecoder().decode(stringBuilder.toString());

        stream.write(base64);
   } catch .... / omitted part of code
}

Can anyone suggest how to solve out of memory issue?谁能建议如何解决内存不足问题? Stack trace:堆栈跟踪:

Caused by: java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:3332)
        at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
        at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:596)
        at java.lang.StringBuilder.append(StringBuilder.java:190)
        at com....AttachmentHandler.characters(AttachmentHandler.java:47)

EDIT:编辑:

Base64 is from XML: Base64 来自 XML:

<value> String encoded with base64 </value>

For SAX parser I am using RestTemplate:对于 SAX 解析器,我使用的是 RestTemplate:

@Override
@Retryable
public Application performMultimediaRequest(String url) {

    SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();

    try {
        SAXParser saxParser = saxParserFactory.newSAXParser();

        restTemplate.execute(url,
                HttpMethod.GET,
                null,
                responseExtractor -> {
                    try {
                        saxParser.parse(responseExtractor.getBody(), attachmentHandler);
                    } catch (SAXException e) {
                        log.error("Cannot parse API response with SAX parser");
                    }
                    return null;
                }
        );
    } catch (SAXException | ParserConfigurationException e) {
        log.error("Cannot parse response with SAX parser");
    }

    return attachmentHandler.getApplication();
}

Either increase the JVM heap memory to accommodate StringBuilder memory requirements or use Base64.getDecorder().wrap(InputStream) method:要么增加 JVM 堆内存以满足StringBuilder内存要求,要么使用Base64.getDecorder().wrap(InputStream)方法:

Returns an input stream for decoding Base64 encoded byte stream.返回用于解码 Base64 编码字节流的输入流。

The read methods of the returned InputStream will throw IOException when reading bytes that cannot be decoded.返回的 InputStream 的 read 方法在读取无法解码的字节时会抛出 IOException。

Closing the returned input stream will close the underlying input stream.关闭返回的输入流将关闭底层输入流。

The problem with your example code is that it's incomplete and we don't know from where you are getting the char value.您的示例代码的问题在于它不完整,我们不知道您从哪里获得char值。 If you are getting the char value from InputStream you have the work cut out for wrap() .如果您从InputStream获取char值,您就可以完成wrap()

You can hope to decrease the overall memory requirements by switching from Base64.getDecorder().decode(String) to Base64.getDecorder().decode(byte[]) but if you want to save the heap memory you should rewrite your code to use InputStream and don't create temporary String or byte[] .您可以希望通过从Base64.getDecorder().decode(String)切换到Base64.getDecorder().decode(byte[])来降低整体内存需求,但是如果您想节省堆内存,您应该将代码重写为使用InputStream并且不要创建临时Stringbyte[]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM