加密期间Java内存不足错误 [英] Java Out of Memory Error during Encryption

查看:68
本文介绍了加密期间Java内存不足错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用AES加密文件.当我尝试加密大文件时,问题首先出现.所以我做了一些在线阅读,发现我需要使用一个缓冲区,并且一次只加密数据字节.

I am using AES to encrypt files. The problem first came when i tried to encrypt a large file. So i did some reading online and figured that i need to use a buffer and only encrypt bytes of data at a time.

我将纯文本分为8192个字节的数据块,然后对每个这些块应用加密操作,但仍然遇到内存不足错误.

I divided my plaintext into chunks of 8192 bytes of data and then applied the encryption operation on each of these chunks but I am still getting the out of memory error.

public static  File encrypt(File f, byte[] key) throws Exception
{
    System.out.println("Starting Encryption");
    byte[] plainText = fileToByte(f);
    SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM);
    Cipher cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);

    System.out.println(plainText.length);

    List<byte[]> bufferedFile = divideArray(plainText, 8192);


    System.out.println(bufferedFile.size());

    List<byte[]> resultByteList = new ArrayList<>();

    for(int i = 0; i < bufferedFile.size(); i++)
    {
        resultByteList.add(cipher.doFinal(bufferedFile.get(i)));
    }

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    for(byte[] b : resultByteList)
        baos.write(b);

    byte[] cipherText = baos.toByteArray();

    File temp = byteToFile(cipherText, "D:\\temp");

    return temp;

}

fileToByte()将文件作为输入并返回字节数组

The fileToByte() takes a file as input and returns a byte array

divideArray()将字节数组作为输入,并将其划分为由较小字节数组组成的arraylist.

the divideArray() takes a byte array as input and divides it into an arraylist consisting of smaller byte arrays.

public static List<byte[]> divideArray(byte[] source, int chunkSize) {

    List<byte[]> result = new ArrayList<byte[]>();
    int start = 0;
    while (start < source.length) {
        int end = Math.min(source.length, start + chunkSize);
        result.add(Arrays.copyOfRange(source, start, end));
        start += chunkSize;
    }

    return result;
}

这是我得到的错误

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3236)
at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
at java.io.OutputStream.write(OutputStream.java:75)
at MajorProjectTest.encrypt(MajorProjectTest.java:61)
at MajorProjectTest.main(MajorProjectTest.java:30)

如果我使用较小的文件,则不会出现此错误,但是再次,使用缓冲区的唯一目的是消除内存不足的问题.

I am not getting this error if I use a file of a smaller size, but then again, the sole purpose of using buffers was to eliminate the out of memory problem.

先谢谢了.感谢您的帮助.

Thanks in advance. Any help is appreciated.

推荐答案

一个问题是将数组和数组副本保存在内存中.

One problem is holding arrays and copies of arrays in memory.

读写块.

然后,不应重复 doFinal .请改用 update .许多示例仅使用单个 doFinal 就会产生误导.

Then doFinal should not be repeated. Use update instead. Many examples just use a single doFinal which is misleading.

所以:

public static  File encrypt(File f, byte[] key) throws Exception
{
    System.out.println("Starting Encryption");
    SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM);
    Cipher cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);

    System.out.println(plainText.length);

    Path outPath = Paths.get("D:/Temp");
    byte[] plainBuf = new byte[8192];
    try (InputStream in = Files.newInputStream(f.toPath());
            OutputStream out = Files.newOutputStream(outPath)) {
        int nread;
        while ((nread = in.read(plainBuf)) > 0) {
            byte[] enc = cipher.update(plainBuf, 0, nread);
            out.write(enc);
        }       
        byte[] enc = cipher.doFinal();
        out.write(enc);
    }
    return outPath.toFile();
}


说明


Explanation

某些字节块的加密方式如下:

Encryption of some byte blocks goes as:

  • Cipher.init
  • Cipher.update块[0]
  • Cipher.update块[1]
  • Cipher.update块[2]
  • ...
  • Cipher.doFinal(block [n-1])

或者代替最后一个doFinal:

Or instead of the last doFinal:

  • Cipher.update(block [n-1])
  • Cipher.doFinal()

每次 update doFinal 都会产生一部分加密数据.

Every update or doFinal yielding a portion of the encrypted data.

doFinal 还会刷新"最终的加密数据.

doFinal also "flushes" final encryption data.

如果只有一个字节块,则足以调用

If one has only a single block of bytes, it suffices to call

byte[] encryptedBlock = cipher.doFinal(plainBlock);

然后不需要调用 cipher.update .

对于其余的内容,我使用了try-with-resources语法,即使发生 return 或引发了异常,它也会自动关闭输入和输出流.

For the rest I used the try-with-resources syntax which automatically closes input and output streams, even should a return happen, or an exception have been thrown.

更新的 Path 代替了 File ,具有更多用途,并且与 Paths.get("...")结合使用非常好实用程序类 Files 可以提供强大的代码:例如 Files.readAllBytes(path)等.

Instead of File the newer Path is a bit more versatile, and in combination with Paths.get("...") and the very nice utility class Files can provide powerful code: like Files.readAllBytes(path) and much more.

这篇关于加密期间Java内存不足错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆