Copying a large stream to String - Java
我正在用Java编写
一切都很好,但是我正在(相对)处理大型对象。结果对象约为25 MB(在使用String表示之前)。我将其作为Applet运行,因此我有66 MB的堆空间,很快就用尽了。
到目前为止,我已经尝试了几种方法:
第一个可以工作到大约11 MB,这时旧的String和新的String在连接时会占用过多的空间。
第二个完全失败,只有大约7 MB。
第三个(也许是最好的),它存储了整个流,但是毫无疑问,当尝试获取String时,它失败了。
我将如何进行这项工作?有可能吗?
我认为我有足够的空间容纳结果String,但这是问题所在的复制(因为您需要传统副本的源和目标)。我知道字符串是不可变的,但是有什么方法可以将一些字符附加到末尾?
这是我的三个示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | 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; } } |
关于代码的一些注释:显然,您重命名了要用于write()和toString()的方法。同样,字节数组是(当前)静态分配的,但是如果我走那条路线(并且在其他方??法中没有使用),那将会改变。
编辑1:
有关我的整体问题的更多信息:
这是一个较大的应用程序的一部分,该应用程序将数据收集,签名并上传到服务器。我必须读取一个文件,对其进行SHA-1哈希处理,对其进行加密,然后构造一个XML文档(其中还包含其他一些内容,例如时间)。然后必须对该XML文档进行签名(通过XML DSig,又名
要签名的文件从1KB到大约50 MB。
有几个问题:
当您将结果字符串放入XML文档时,建议您使用流XML api。这样就可以传输整个过程,而您无需在内存中保留大量数据。
XML文档发生了什么?作为一个applet,我可以想象只有几种选择-写到沙箱中的文件,或流回到原始服务器。如果使用流XML,则可以在通过流写入数据时将数据发送到其最终位置来完成。
例如,您可以将字符数据流传输到StringOutputStream中的SAX ContentHandler,而不是将数据存储在缓冲区中。
编辑1:
鉴于最大文件大小为50MB,我认为您将applet推得太远了,除非可以保证它们配置的内存是最大文件大小的3-4倍(例如,在Windows上使用java控制面板插件)。 )登录小程序不是很安全-进行逆向工程很容易,并且容易获得使签名不可信的私钥。如果小程序始终使用相同的密钥,那么您是否可以移动签名服务器端?该文件仍在上载,这将避免所有内存问题。该方案是:
- 小程序将原始文件上传到服务器
- 服务器从文件创建XML
- 服务器对XML进行签名
- 服务器将转发已签名的XML到小程序正在发送的XML。如果是您自己的服务器/ Web应用程序之一,则该文件已经可以使用。
感谢mdma,我意识到我真的需要一种流式传输方法,而不是存储在内存中。
这就是我的工作:小程序现在像以前一样加密数据,但是使用PKCS7(使用BouncyCastle \\的
然后将由PKCS7签名生成的分离的PKCS7签名放入XML内。然后使用XML DSig对XML进行签名,并将其分别上传到服务器。