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

时间:2012-11-07 16:52:21

标签: java file file-io

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

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

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

    IOUtils.toByteArray(InputStream input).

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

    Arrays.copyOfRange()

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

2 个答案:

答案 0 :(得分:5)

在Java中查找密码流。您可以使用它们来动态加密/解密流,这样您就不必将整个内容存储在内存中。您所要做的就是将源文件的常规FileInputStream复制到包含CipherOutputStream加密接收器文件的FileOutputStreamIOUtils甚至可以方便地包含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上。但是,您不必担心任何,因为它都是内部的,所有内容都是为您管理的。

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

  

此类严格遵守其祖先类java.io.OutputStreamjava.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,可选择跳过输入字节。