【问题标题】:Base64 value causing StringBuilder.append out of memory error in SAX parserBase64 值导致 SAX 解析器中的 StringBuilder.append 内存不足错误
【发布时间】:2020-07-11 03:50:41
【问题描述】:

我正在使用 SAX 解析器来解析来自客户端服务器的响应的 XML 文件。标签包含文件(jpg、png、pdf 等)的 Base64 解码值。其中一些我可以很容易地写入文件,但其中一些(我猜是大的)我在 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
    }
}

在接下来的步骤中,我正在使用 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
}

谁能建议如何解决内存不足的问题?堆栈跟踪:

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)

编辑:

Base64 来自 XML:

<value> String encoded with base64 </value>

对于 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();
}

【问题讨论】:

    标签: java xml-parsing base64 stringbuilder saxparser


    【解决方案1】:

    要么增加 JVM 堆内存以满足 StringBuilder 内存需求,要么使用 Base64.getDecorder().wrap(InputStream) 方法:

    返回一个用于解码 Base64 编码字节流的输入流。

    返回的InputStream的read方法在读取无法解码的字节时会抛出IOException。

    关闭返回的输入流将关闭底层输入流。

    您的示例代码的问题在于它不完整,我们不知道您从哪里获得 char 值。如果您从InputStream 获得char 值,那么您需要为wrap() 完成工作。

    您可以希望通过从Base64.getDecorder().decode(String) 切换到Base64.getDecorder().decode(byte[]) 来减少整体内存需求,但是如果您想节省堆内存,您应该重写代码以使用InputStream,并且不要创建临时的Stringbyte[]

    【讨论】:

    • 我刚刚编辑了问题并添加了 Base64 从何处进入 SAX 解析器。当值标记处于活动状态时,我正在使用 SAX 解析器的字符方法,正如您已经提到的那样,使用 InputStream 会更好,但是,我在实现它时遇到了一些问题。详细地说,我不知道如何将来自 SAX 的 char 数组添加到 characters 方法的一个 InputStream 中。此外,当我稍后将使用 Base64.getDecorder().wrap(InputStream) 时,我将获得 InputStream,而不是 byte[],我可以将其与我的文件一起添加到 OutputStream。到目前为止,感谢您的一些建议。
    猜你喜欢
    • 1970-01-01
    • 2011-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-02
    • 1970-01-01
    • 2015-02-15
    • 1970-01-01
    相关资源
    最近更新 更多