2010-06-09 5 views
2

문자열로 이동하기 위해 OutputStream에서 pre-Base64로 인코딩 된 데이터가 필요하기 때문에 Java에서 StringOutputStream 클래스를 작성하고 있습니다. 이것을 W3C XML Document에 넣으므로 String으로 필요합니다.문자열로 큰 스트림 복사 -

모든 것이 좋겠지 만 (상대적으로) 큰 개체를 다루고 있습니다. 결과 객체는 (문자열 표현 이전에) 약 25MB로 밝혀졌습니다. 애플릿으로 실행 중이므로 66 MB의 힙 공간이 매우 빨리 소모됩니다.

는 지금까지 몇 가지 방법을 시도 :

  1. 이 String 객체에 수신 된 바이트를 추가 (strObj.concat((byte) b)strObj += new String((byte) b) 사용) 및
  2. 를 버퍼링없이 StringBuffer
  3. 에 수신 된 바이트 추가 바이트를 바이트 배열에 추가 한 다음 문자열을 원할 때 해당 바이트 배열을 문자열로 변환

숫자 1은 abo까지 작동합니다. ut 11 MB, 이전 String과 새 String이 연결될 때 너무 많은 공간을 사용하는 경우.

2 번은 총 실패로 약 7MB에 불과합니다.

3 번이 (아마도?) 가장 좋았습니다. 전체 스트림을 저장하지만, 문자열을 얻으려고하면 당연히 실패합니다.

어떻게하면됩니까? 가능한가?

나는 결과 문자열을 저장할 수있는 공간을 가지고 있다고 생각하지만 문제는 복사입니다 (기존 사본의 원본과 대상이 필요하므로). 나는 문자열이 변경되지 않는다는 것을 알고 있지만, 끝에 문자를 추가하는 방법이 있습니까?

은 여기 내 세 가지 예를의 : 코드에 대한

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()를 작성하는 데 사용할 방법을 변경합니다. 또한, 바이트 배열은 (현재) 정적으로 할당되어 있지만 그 경로로 이동하면 (다른 메소드에서는 사용되지 않음) 변경됩니다.

편집 1 : 내 전반적인 문제에 더 많은 정보 :

이 서버에 데이터 표시를하고 업로드를 취하는 큰 응용 프로그램의 일부입니다. 나는 파일을 읽고, SHA-1 해쉬를 가져 와서 암호화하고, XML 문서 (시간과 같은 몇 가지 다른 것들을 포함하여)를 만들어야한다. 그런 다음 해당 XML 문서는 XML DSig (별칭 : javax.xml.crypto.dsig.XMLSignatureFactory)를 통해 서명하고 서버에 다시 업로드해야합니다.

서명 할 파일의 크기는 1KB에서 50MB 사이입니다.

  1. XML DSIG의 현재 자바 구현 구문 분석 및 XML하지 않는 스트림, 단지 W3C 노드 :

    은 몇 가지 문제가 있습니다.(나는 다른 구현을 찾을 수 없다.)

  2. 내 상사는 최소한의 클라이언트 측 설치가 필요하지 않으므로 애플릿이 선택된 이유는 서명 된 애플릿이므로 클라이언트의 모든 항목에 액세스 할 수 있기 때문입니다. .
+0

'java.io.ByteArrayOutputStream' 또는'java.io.StringWriter'가 효과가없는 이유를 설명 할 수 있습니까? – Pointy

+0

ByteArrayOutputStream은 메서드 3과 똑같습니다. 모든 것을 복사 한 다음 toString을 호출 할 때 메모리가 부족합니다. StringWriter는 메소드 2와 거의 동일한 10MB에서 메모리가 부족합니다. – HalfBrian

+0

해결하려는 문제에 대해 질문에 더 추가 할 수 있습니까? 이것은 모든 사람이 상자 밖에서 생각하고 다른 각도에서 문제를 해결하는 데 도움이 될 수 있습니다. 예 : 22MB의 데이터에 초점을 맞 춥니 다. 서명 된 데이터가 고정되어 있습니까? 크기에 대한 상한선입니까? – mdma

답변

0

mdma 덕분에 메모리에 저장하는 대신 실제로 스트림하는 방법이 필요하다는 것을 깨달았습니다.

내가 한 일은 애플릿이 이전처럼 데이터를 암호화하지만 BouncyCastle의 CMSSignedDataStreamGenerator을 사용하여 PKCS7을 사용하여 서명합니다. 데이터는 클라이언트의 컴퓨터에 저장하지 않고 네트워크를 통해 스트리밍됩니다.

PKCS7 서명에 의해 생성 된 분리 된 PKCS7 서명이 XML에 저장됩니다. 그런 다음 XML은 XML DSig로 서명되고 별도로 서버에 업로드됩니다.

3

결과 문자열을 XML 문서에 넣을 때 스트리밍 XML API를 사용하는 것이 좋습니다. 그런 다음 전체 프로세스를 스트리밍 할 수 있으므로 많은 양의 데이터를 메모리에 보관할 필요가 없습니다.

XML 문서에 어떤 현상이 있습니까? 애플릿이므로 샌드 박스의 파일에 쓰거나 원본 서버로 다시 스트리밍하는 몇 가지 대안 만 상상할 수 있습니다. 스트리밍 XML을 사용하는 경우 스트림을 통해 데이터를 쓸 때 데이터가 최종 위치로 전송되므로이 작업을 수행 할 수 있습니다.

예를 들어 버퍼에 데이터를 저장하는 대신 문자 데이터를 StringOutputStream의 SAX ContentHandler에 스트리밍 할 수 있습니다.

편집 1 : 50메가바이트의 최대 파일 크기를 감안할 때

, 당신이 밀고 생각은 예를 들어, 사용 (당신은 그들이 당신의 최대 파일 크기를 3-4 배 ca를 메모리로 구성되어 보장 할 수없는 한, 너무 멀리 조금 애플릿 Windows의 Java 제어판 플러그인). 애플릿에 서명하는 것은 매우 안전하지 않습니다. 리버스 엔지니어링을 수행하고 서명을 신뢰할 수 없게 만드는 개인 키를 얻는 것은 쉽습니다. 애플릿이 항상 동일한 키를 사용하는 경우 서명 서버 측을 이동할 수 있습니까? 파일은 어쨌든 업로드되고 있으며 이로 인해 모든 메모리 문제가 해결됩니다. 계획은 다음과 같습니다

  • 애플릿이 서버
  • 서버에 원본 파일을 업로드 파일 서버가 XML
  • 단절이 whever하는 서명 된 XML을 전달에게 서명
  • 에서 XML을 생성 애플릿이 보내고있었습니다. 자신의 서버/웹 응용 프로그램 중 하나에 있다면 파일은 이미 사용할 수 있습니다.
+0

나는 여전히 스트리밍 XML API를 사용하고 있지만 문제는 XML에 서명해야한다는 것입니다. XMLSignatureFactory를 사용하여 스트리밍 XML API를 사용하여이를 수행 할 수 있는지를 확인하지 못했습니다. – HalfBrian

+0

Java Web Start 및 옵션을 사용하고 있습니까? VM 시작시 필요한 메모리 양을 지정할 수 있습니다. – mdma

+0

불행히도 내 상사가로드 가능한 애플리케이션을 필요로하지 않기 때문에 불행히도 그렇지 않습니다. 이 문제를 해결할 수 없다면 분명히 버려지는 제한이 될 것입니다. 그런 다음 데스크톱 응용 프로그램으로 실행할 것입니다. – HalfBrian

관련 문제