Java加密大文件触发java.lang.OutOfMemoryError堆空间异常的解决方案
Hey there! Let's fix that OutOfMemoryError you're facing with large video files. The root cause is obvious in your current code: you're loading the entire file into a single byte array all at once. That works for small files, but an 800MB file will gobble up all your heap space instantly.
Luckily, you already pointed to the right solution: block-by-block processing. Instead of reading the whole file into memory, we'll stream it in small chunks, encrypt/decrypt each chunk, and write it out immediately. Since AES uses 16-byte blocks, our buffer should play nicely with that (though the Cipher API handles partial blocks and padding automatically).
Here's the revised CryptoUtils.java with the memory-efficient fix:
private static final String ALGORITHM = "AES"; private static final String TRANSFORMATION = "AES"; // 8KB buffer is a balanced choice—adjust if needed, multiples of 16 work well private static final int BUFFER_SIZE = 8192; public static void encrypt(String key, File inputFile, File outputFile) throws CryptoException { doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile); } public static void decrypt(String key, File inputFile, File outputFile) throws CryptoException { doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile); } private static void doCrypto(int cipherMode, String key, File inputFile, File outputFile) throws CryptoException { try { Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM); Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(cipherMode, secretKey); // Use try-with-resources to auto-close streams and avoid leaks try (FileInputStream inputStream = new FileInputStream(inputFile); FileOutputStream outputStream = new FileOutputStream(outputFile)) { byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead; // Process the file in chunks while ((bytesRead = inputStream.read(buffer)) != -1) { byte[] processedBytes = cipher.update(buffer, 0, bytesRead); if (processedBytes != null) { outputStream.write(processedBytes); } } // Handle final block (includes padding cleanup for decryption) byte[] finalBytes = cipher.doFinal(); if (finalBytes != null) { outputStream.write(finalBytes); } } } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException | IOException ex) { throw new CryptoException("Error encrypting/decrypting file", ex); } }
A few key improvements here:
- Streamed processing: Only a small 8KB chunk of the file lives in memory at any time, so heap usage stays low no matter how big your video file is.
- Try-with-resources: Automatically closes input/output streams, so you don't have to worry about resource leaks even if an exception pops up.
- Proper Cipher usage:
cipher.update()handles most of the data, whilecipher.doFinal()takes care of the last block and padding (critical for correct decryption).
Quick side note for security: Using "AES" as your transformation defaults to ECB mode, which is not secure for most use cases. For better protection, switch to a mode like CBC or GCM (e.g., "AES/CBC/PKCS5Padding"). Just remember that modes like CBC require an initialization vector (IV) — you'll need to generate one during encryption, store it with the encrypted file, and use it again during decryption. But that's a security upgrade; the above code first fixes your immediate memory issue.
Give this a test run — your large video files should process without hitting heap space errors now!
内容的提问来源于stack exchange,提问作者kenn

