декодировать большой base64 из XML в Java: OutOfMemory - PullRequest
3 голосов
/ 29 апреля 2011

Мне нужно записать элемент x64 в кодировке base64 в отдельный файл. Проблема: размер файла может достигать 100 МБ. Каждое решение, которое я пробовал, заканчивалось «java.lang.OutOfMemoryError: Java heap space». Проблема не в чтении xml в целом или в процессе декодирования, а в размере блока base64.

Я использовал jdom, dom4j и XMLStreamReader для доступа к XML-файлу. Однако, как только я хочу получить доступ к содержимому base64 соответствующего элемента, я получаю упомянутую ошибку. Я также попробовал xslt, используя саксонскую функцию base64Binary-to-octets, но, конечно, с тем же результатом.

Есть ли способ для потоковой передачи этой части, закодированной в base64, в файл, не получая весь кусок целиком?

Спасибо за ваши подсказки,

Andreas

Ответы [ 5 ]

2 голосов
/ 29 апреля 2011

Кодек Apache Commons имеет Base64OutputStream, который должен позволять вам масштабировать данные XML, объединяя Base64OutputStream с FileOutputStream.

Вам понадобится представление XML в виде строки, поэтому вам даже не нужно будет читать его в структуре DOM.

Что-то вроде:

PrintWriter printWriter = new PrintWriter(
   new Base64OutputStream(
      new BufferedOutputStream(
         new FileOutputStream("/path/to/my/file")
      )
   )
);
printWriter.write(myXml);
printWriter.close();

Если входной XML-файл слишком велик, то вы должны считать его фрагменты в буфер в цикле, записав содержимое буфера в выходной файл (т. Е. Стандартную копию для чтения-записи).

1 голос
/ 29 апреля 2011

Я не думаю, что какой-либо API-интерфейс XML позволит вам получить доступ к тексту элемента в виде потока, а не строки. Если значение String составляет 100 МБ, то, возможно, единственным вариантом является изменение размера кучи JVM до тех пор, пока у вас не будет OutOfMemoryError:

java -Xmx256m your.class.Name
0 голосов
/ 29 апреля 2011

Как сказал lbruder , используйте синтаксический анализатор SAX для чтения документа в потоковом режиме. Если вы используете Base64OutputStream , вам нужно установить флаг, чтобы он ДЕКОДИРОВАЛ вместо стандартного КОДЕКСА. Вы также должны преобразовать массив char из обратного вызова символов в байтовый массив, прежде чем передавать его в выходной поток, требуя дополнительных выделений памяти и копий.

Я написал альтернативный декодер base64 именно для этого варианта использования, он доступен по адресу github . Вот пример того, как его использовать:

Base64StreamDecoder decoder = new Base64StreamDecoder();
OutputStream out;

...

public void startElement(String uri, String localName, String qName, Attributes atts) {
    decoder.reset();
    out = new BufferedOutputStream(new FileOutputStream(...));
}

public void endElement(String uri, String localName, String qName) {
    decoder.checkComplete();
    out.close();
}

public void characters(char[] ch, int start, int length) {
    decoder.decode(ch, start, length, out);
}
0 голосов
/ 29 апреля 2011

Попробуйте API StAX ( учебник ). Для больших текстовых элементов вы должны получить несколько текстовых событий, которые вам нужно вставить в потоковую реализацию Base64 (как упомянутое выше упомянутое skaffman).

0 голосов
/ 29 апреля 2011

Если ваш файл может стать таким большим, никогда не используйте анализатор DOM. Используйте простой подход SAX для доступа к элементам данных и потоковой передачи данных base64 в Base64OutputStream, как указано выше.

...