Значение Base64, вызывающее ошибку StringBuilder.append из памяти в SAX-парсере - PullRequest
0 голосов
/ 30 марта 2020

Я использую SAX-парсер для разбора файла XML, который является ответом с сервера клиента. тег имеет внутри Base64 декодированное значение файлов (jpg, png, pdf et c.). Некоторые из них я могу легко записать в файл, но с некоторыми из них (я предполагаю большой) у меня возникла проблема во время 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();
}

1 Ответ

2 голосов
/ 30 марта 2020

Либо увеличьте объем памяти кучи JVM для соответствия требованиям StringBuilder памяти, либо используйте метод Base64.getDecorder().wrap(InputStream):

Возвращает входной поток для декодирования потока байтов в кодировке Base64.

Методы чтения возвращенного InputStream будут генерировать IOException при чтении байтов, которые не могут быть декодированы.

Закрытие возвращенного входного потока закроет основной входной поток.

Проблема с вашим примером кода состоит в том, что он неполон, и мы не знаем, откуда вы получаете значение char. Если вы получаете char значение от InputStream, у вас есть работа, выключенная для wrap().

. Вы можете надеяться уменьшить общие требования к памяти, переключившись с Base64.getDecorder().decode(String) на Base64.getDecorder().decode(byte[]), но если вы хотите сохранить кучу памяти, вы должны переписать свой код для использования InputStream и не создавать временные String или byte[].

...