6

我有一个加密算法 (AES),它接受转换为数组字节的文件并对其进行加密。由于我要处理非常大的文件,JVM 可能会出现内存不足。我打算读取多个字节数组中的文件,每个字节数组都包含文件的某些部分。然后我迭代地输入算法。最后,我将它们合并生成一个加密文件。

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

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

    IOUtils.toByteArray(InputStream input).

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

    Arrays.copyOfRange()

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

4

2 回答 2

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而不是IOUtils.copy文件大小超过Integer.MAX_VALUE字节(2 GB)。

要解密文件,请执行相同的操作,但使用CipherInputStream 代替CipherOutputStream并初始化您的Cipherusing Cipher.DECRYPT_MODE

在此处查看有关 Java 密码流的更多信息。

这将为您节省空间,因为您不再需要存储byte自己的数组。该系统中唯一存储byte[]的是 的内部byte[]Cipher每次输入足够的输入并返回一个加密块时,它都会被清除Cipher.update,或者在关闭Cipher.doFinal时会被清除CipherOutputStream。但是,您不必担心任何这些,因为这一切都是内部的,一切都是为您管理的。

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

该类严格遵守其祖先类的语义,尤其是失败语义,java.io.OutputStream并且java.io.FilterOutputStream. 此类具有在其祖先类中指定的那些方法,并覆盖它们。此外,此类捕获其祖先类未引发的所有异常。

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

于 2012-11-07T17:32:42.707 回答
1

如果您正在使用IOUtils,也许您应该考虑IOUtils.copyLarge()

public static long copyLarge(InputStream input,
                             OutputStream output,
                             long inputOffset,
                             long length)

并指定一个ByteArrayOutputStream作为输出。然后,您可以使用偏移量/长度迭代和加载文件的各个部分。

从文档:

将大(超过 2GB)InputStream 中的部分或全部字节复制到 OutputStream,可选择跳过输入字节。

于 2012-11-07T16:58:58.097 回答