我有一个加密算法(AES),它接受一个转换为数组字节的文件并加密它。 由于我要处理非常大的文件,JVM可能会耗尽内存。 我计划读取多个字节数组中的文件,每个数组包含文件的某些部分。然后我迭代地提供算法。最后,我将它们合并以生成加密文件。
所以我的问题是:有没有办法将文件按部分读取到多个字节数组?
我以为我可以使用以下内容将文件读取为字节数组:
IOUtils.toByteArray(InputStream input).
然后使用以下方法将数组拆分为多个字节:
Arrays.copyOfRange()
但我担心将文件读取到ByteArray
的代码会使JVM内存不足。
答案 0 :(得分:5)
在Java中查找密码流。您可以使用它们来动态加密/解密流,这样您就不必将整个内容存储在内存中。您所要做的就是将源文件的常规FileInputStream
复制到包含CipherOutputStream
加密接收器文件的FileOutputStream
。 IOUtils
甚至可以方便地包含copy(InputStream, OutputStream)
方法来为您执行此副本。
例如:
public static void main(String[] args) {
encryptFile("exampleInput.txt", "exampleOutput.txt");
}
public static void encryptFile(String source, String sink) {
FileInputStream fis = null;
try {
fis = new FileInputStream(source);
CipherOutputStream cos = null;
try {
cos = new CipherOutputStream(new FileOutputStream(sink), getEncryptionCipher());
IOUtils.copy(fis, cos);
} finally {
if (cos != null)
cos.close();
}
} finally {
if (fis != null)
fis.close();
}
}
private static Cipher getEncryptionCipher() {
// Create AES cipher with whatever padding and other properties you want
Cipher cipher = ... ;
// Create AES secret key
Key key = ... ;
cipher.init(Cipher.ENCRYPT_MODE, key);
}
如果您需要知道复制的字节数,如果文件大小超过IOUtils.copyLarge
字节(2 GB),则可以使用IOUtils.copy
而不是Integer.MAX_VALUE
。
要解密文件,请执行相同操作,但使用CipherInputStream
代替 CipherOutputStream
并使用Cipher
初始化Cipher.DECRYPT_MODE
。< / p>
有关Java中密码流的更多信息,请查看here。
这样可以节省空间,因为您不再需要存储自己的byte
数组。此系统中唯一存储的byte[]
是byte[]
的内部Cipher
,每次输入足够的输入并且Cipher.update
返回加密的块时,它将被清除,当Cipher.doFinal
关闭时,或CipherOutputStream
上。但是,您不必担心任何,因为它都是内部的,所有内容都是为您管理的。
修改:请注意,这可能导致忽略某些加密异常,尤其是BadPaddingException
和IllegalBlockSizeException
。可以在CipherOutputStream
source code中找到此行为。 (当然,这个源来自OpenJDK,但它可能在Sun JDK中做同样的事情。)另外,来自CipherOutputStream javadocs:
此类严格遵守其祖先类
java.io.OutputStream
和java.io.FilterOutputStream
的语义,尤其是失败语义。该类具有其祖先类中指定的那些方法,并将它们全部覆盖。 此外,此类捕获其祖先类未抛出的所有异常。
这里的粗体线意味着忽略了加密异常,它们就是这样。这可能会在尝试读取加密文件时导致一些意外行为,尤其是对于块和/或填充加密算法(如AES)。请记住这一点,您将获得加密(或解密为CipherInputStream
)文件的零或部分输出。
答案 1 :(得分:1)
如果您使用IOUtils
,也许您应该考虑IOUtils.copyLarge()
public static long copyLarge(InputStream input,
OutputStream output,
long inputOffset,
long length)
并指定ByteArrayOutputStream作为输出。然后,您可以使用偏移/长度迭代并加载文件的各个部分。
来自doc:
将大型(超过2GB)InputStream中的部分或全部字节复制到 OutputStream,可选择跳过输入字节。