将文件读取到多个字节数组

LeT*_*Tex 6 java file-io file

我有一个加密算法(AES)接受转换为数组字节的文件并加密它.由于我要处理非常大的文件,JVM可能会耗尽内存.我计划读取多个字节数组中的文件,每个数组包含文件的某些部分.然后我迭代地提供算法.最后,我将它们合并以生成加密文件.

所以我的问题是:有没有办法将文件逐个读取到多个字节数组?

我以为我可以使用以下内容将文件读取为字节数组:

    IOUtils.toByteArray(InputStream input).
Run Code Online (Sandbox Code Playgroud)

然后使用以下命令将数组拆分为多个字节:

    Arrays.copyOfRange()
Run Code Online (Sandbox Code Playgroud)

但我担心读取文件的代码ByteArray会使JVM内存不足.

Bri*_*ian 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);
}
Run Code Online (Sandbox Code Playgroud)

如果您需要知道复制的字节数,则可以使用IOUtils.copyLarge而不是IOUtils.copy文件大小超过Integer.MAX_VALUE字节(2 GB).

要解密文件,请执行相同的操作,但请使用CipherInputStream 而不是CipherOutputStream初始化您的Cipher使用Cipher.DECRYPT_MODE.

有关Java中密码流的更多信息,请查看此处.

这将节省您的空间,因为您不再需要存储byte自己的数组.唯一的存储byte[]在本系统中是内部byte[]Cipher,这将得到每个足够的输入被输入和一个加密的块被返回时间清零Cipher.update,或Cipher.doFinalCipherOutputStream关闭.但是,您不必担心任何此问题,因为它都是内部的,所有内容都是为您管理的.

编辑:请注意,这可能会导致某些加密例外被忽略,特别是BadPaddingExceptionIllegalBlockSizeException.可以在CipherOutputStream源代码中找到此行为.(当然,这个源来自OpenJDK,但它可能在Sun JDK中做同样的事情.)另外,来自CipherOutputStream的 javadocs:

这个类严格遵守其祖先类java.io.OutputStream和语义的语义,特别是失败语义java.io.FilterOutputStream.该类具有其祖先类中指定的那些方法,并将它们全部覆盖.此外,此类捕获其祖先类未抛出的所有异常.

这里的粗体线意味着忽略了加密异常,它们就是这样.这可能会在尝试读取加密文件时导致一些意外行为,尤其是对于块和/或填充加密算法(如AES).请记住这一点,您将获得加密(或解密CipherInputStream)文件的零或部分输出.