Копирование большого потока в String - Java - PullRequest
2 голосов
/ 09 июня 2010

Я пишу класс StringOutputStream на Java, потому что мне нужны данные в кодировке до Base64 из OutputStream, чтобы перейти к строке.Мне нужно это как String, потому что я собираюсь поместить его в W3C XML Document.

Все бы хорошо, но я имею дело с (относительно) большими объектами.Полученный объект оказывается около 25 МБ (до представления String).Я запускаю это как апплет, поэтому у меня есть 66 МБ пространства кучи, которое быстро истощается.

Я уже пробовал несколько методов:

  1. Добавить полученный байтк объекту String (используя strObj.concat((byte) b) и strObj += new String((byte) b)) с буферизацией и без нее
  2. Добавьте полученный байт к StringBuffer
  3. Добавьте байт в байтовый массив, затем, когдаТребуется строка, преобразуйте этот байтовый массив в строку

Номер один работает примерно до 11 МБ, когда старая строка и новая строка занимают слишком много места при объединении.

Номер два был полным провалом, он получает только около 7 МБ.

Номер три был (возможно?) Лучшим, он хранит весь поток, но при попытке получить его String, что неудивительно,не удается.

Как бы я сделал эту работу?Возможно ли это?

Я думаю, у меня есть свободное место для хранения результирующей строки, но проблема заключается в копировании (поскольку для традиционной копии вам нужны источник и место назначения).Я знаю, что строки неизменяемы, но есть ли способ добавить некоторые символы в конец?

Вот три моих примера:

package com.myorg.SigningServer.Util.Security;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;

import com.technicolor.SigningServer.Applet.SigningApplet;

public class StringOutputStream extends OutputStream {

byte[] array = new byte[1024*1024*22];
StringBuffer sb = new StringBuffer();
String output = "";
int prevByte = -1;
long numBytes = 0;

int bufferPos = 0;
int bufferSize = 512*1024;
byte[] buffer = new byte[bufferSize];

public void write2(int b) throws IOException {
    sb.append((byte) b);
}


public void write3(int b) throws IOException {
    array[(int) numBytes] = (byte) b;
    numBytes++;
}

public void write1(int b) throws IOException {
    numBytes++;
    bufferPos++;
    buffer[bufferPos] = (byte) b;
    if(bufferPos == bufferSize-1)
    {
        bufferPos = 0;
        System.gc();
        System.out.println("Generating string "+numBytes+"; String length "+output.length());
        output = output.concat(new String(buffer));
        System.gc();
    }
}

public void flush1() {
    output = output.concat(new String(Arrays.copyOf(buffer, bufferPos)));
    bufferPos = 0;
    System.gc();
}

public String toString2()
{
    return sb.toString();
}

public String toString3()
{
    return new String(array);
}

public String toString1()
{
    return output;
}
}

Несколько замечаний по поводу кода: очевидно, вы переименовываетеметоды, которые вы хотите использовать для записи () и toString ().Кроме того, байтовый массив (в настоящее время) статически размещен, но это изменится, если я пойду по этому маршруту (и не будет использоваться во время других методов).

Edit 1: Дополнительная информацияо моей общей проблеме:

Это часть более крупного приложения, которое принимает данные, подписывает их и загружает на сервер.Я должен прочитать файл, взять его хэш SHA-1, зашифровать его, а затем создать документ XML (с некоторыми другими вещами, такими как время).Затем этот документ XML должен быть подписан (через XML DSig, он же javax.xml.crypto.dsig.XMLSignatureFactory) и загружен обратно на сервер.

Файлы для подписи имеют размер от 1 КБ до примерно 50 МБ.

Есть несколько проблем:

  1. Текущая реализация Java DSig на Java не анализирует и XML-потоки, только узлы w3c.(Я также не могу найти других реализаций, которые это делают)
  2. Мой начальник хочет, чтобы это не требовало минимальной установки на стороне клиента, поэтому был выбран апплет (это подписанный апплет, поэтому он может получить доступ ко всему наклиент).

Ответы [ 2 ]

3 голосов
/ 09 июня 2010

Поскольку вы помещаете полученную строку в XML-документ, я предлагаю использовать потоковый XML API.Тогда весь процесс может быть передан в потоковом режиме, и вам не нужно хранить большие объемы данных в памяти.

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

Например, вы можете передавать потоковые данные в SAX ContentHandler в вашем StringOutputStream, а не хранить данные в буфере.

РЕДАКТИРОВАТЬ 1:

Учитывая максимальный размер файла 50 МБ, я думаю, что вы толкаете апплеты слишком далеко, если только вы не можете гарантировать, что они сконфигурированы с памятью, в 3-4 раза превышающей ваш максимальный файл(например, с помощью плагина панели управления Java в Windows). Вход в апплет не очень безопасен - его легко перепроектировать и получить закрытый ключ, что делает подпись ненадежной.Если апплет всегда использует один и тот же ключ, вы не можете переместить сервер подписи?Файл все равно загружается, и это позволит избежать всех проблем с памятью.Схема такова:

  • апплет загружает исходный файл на сервер
  • сервер создает XML из файла
  • сервер подписывает XML
  • сервер пересылает подписанный XML-файл, куда бы его ни отправлял апплет.Если это был один из ваших собственных серверов / веб-приложений, то файл уже доступен для использования.
0 голосов
/ 10 июня 2010

Благодаря mdma я понял, что мне действительно нужен способ для потоковой передачи, а не для хранения в памяти.

Вот что я сделал: теперь апплет зашифровывает данные, как и раньше, но подписывает их, используя PKCS7 (используя CMSSignedDataStreamGenerator BouncyCastle). Данные передаются по сети, не сохраняя их на компьютере клиента.

Отдельная подпись PKCS7, сгенерированная подписью PKCS7, затем помещается в XML. Этот XML затем подписывается с помощью XML DSig и отдельно загружается на сервер.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...