You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Apache POI标准加密加密2GB大体积PPTX文件?

Apache POI加密大体积PPTX(2GB含嵌入式视频)的问题与解决方法

问题描述

尝试用Apache POI对2GB的PPTX文件(内含大量嵌入式视频)进行标准密码加密,已配置大文件相关参数:

IOUtils.setByteArrayMaxOverride(Integer.MAX_VALUE);
ZipSecureFile.setMinInflateRatio(-1.0);
ZipInputStreamZipEntrySource.setThresholdBytesForTempFiles(50000000);
ZipPackage.setUseTempFilePackageParts(true);

但仍抛出数组大小超出限制的异常:

Exception in thread "main" org.apache.poi.util.RecordFormatException: Can't allocate an array > 2147483647
        at org.apache.poi.util.IOUtils.safelyAllocateCheck(IOUtils.java:559)
        at org.apache.poi.util.IOUtils.safelyAllocate(IOUtils.java:547)
        at org.apache.poi.poifs.nio.ByteArrayBackedDataSource.extend(ByteArrayBackedDataSource.java:86)
        at org.apache.poi.poifs.nio.ByteArrayBackedDataSource.write(ByteArrayBackedDataSource.java:63)
        at org.apache.poi.poifs.filesystem.POIFSFileSystem.createBlockIfNeeded(POIFSFileSystem.java:493)
        at org.apache.poi.poifs.filesystem.POIFSStream$StreamBlockByteBuffer.createBlockIfNeeded(POIFSStream.java:272)
        at org.apache.poi.poifs.filesystem.POIFSStream$StreamBlockByteBuffer.write(POIFSStream.java:294)
        at org.apache.poi.poifs.filesystem.DocumentOutputStream.write(DocumentOutputStream.java:144)
        at org.apache.poi.poifs.filesystem.DocumentOutputStream.write(DocumentOutputStream.java:124)
        at java.base/java.io.FilterOutputStream.write(FilterOutputStream.java:87)
        at java.base/java.io.FilterOutputStream.write(FilterOutputStream.java:137)
        at org.apache.poi.util.LittleEndianOutputStream.write(LittleEndianOutputStream.java:89)
        at org.apache.poi.util.IOUtils.copy(IOUtils.java:410)
        at org.apache.poi.util.IOUtils.copy(IOUtils.java:387)
        at org.apache.poi.poifs.crypt.standard.StandardEncryptor$StandardCipherOutputStream.processPOIFSWriterEvent(StandardEncryptor.java:201)
        at org.apache.poi.poifs.filesystem.POIFSDocument.<init>(POIFSDocument.java:112)
        at org.apache.poi.poifs.filesystem.DirectoryNode.createDocument(DirectoryNode.java:380)
        at org.apache.poi.poifs.crypt.standard.StandardEncryptor$StandardCipherOutputStream.writeToPOIFS(StandardEncryptor.java:185)
        at org.apache.poi.poifs.crypt.standard.StandardEncryptor$StandardCipherOutputStream.close(StandardEncryptor.java:175)
        at Program.encryptPPTXFile(Program.java:108)

原始加密代码如下:

try(POIFSFileSystem fs = new POIFSFileSystem()) {
    EncryptionInfo info = new EncryptionInfo(EncryptionMode.standard);
    Encryptor encryptor = info.getEncryptor();
    encryptor.confirmPassword(password);

    // Read in an existing OOXML file and write to encrypted output stream
    try (OPCPackage opc = OPCPackage.open(inputPath);
        OutputStream os = encryptor.getDataStream(fs)) {
        opc.save(os);
    }

    // Write out the encrypted version
    try (FileOutputStream fos = new FileOutputStream(outputPath)) {
        fs.writeFilesystem(fos);
    }

    System.out.println("Document successfully encrypted");

} catch (IOException | GeneralSecurityException | InvalidFormatException e) {
    e.printStackTrace();
}

问题核心原因

POIFSFileSystem默认采用内存字节数组存储加密后的全部内容,当文件体积超过2GB时,会触发Java中int类型的最大值限制(2147483647字节),无法分配足够大的内存数组。即使设置了IOUtils.setByteArrayMaxOverride(Integer.MAX_VALUE),也无法突破JVM对单个数组的大小限制,且加密过程的额外内存开销会进一步加剧该问题。

解决方法

改用基于磁盘文件的NPOIFSFileSystem替代内存版的POIFSFileSystem,将加密过程的中间数据存储到临时磁盘文件中,避免内存过载。同时调整OPCPackage的打开模式,减少内存占用。

修改后的加密代码

try (File tempFile = File.createTempFile("poifs", ".tmp");
     NPOIFSFileSystem fs = new NPOIFSFileSystem(tempFile, true)) {
    EncryptionInfo info = new EncryptionInfo(EncryptionMode.standard);
    Encryptor encryptor = info.getEncryptor();
    encryptor.confirmPassword(password);

    // 以文件模式打开OPCPackage,避免加载整个文件到内存
    try (OPCPackage opc = OPCPackage.open(new File(inputPath), PackageAccess.READ_WRITE);
         OutputStream os = encryptor.getDataStream(fs)) {
        opc.save(os);
    }

    // 输出加密后的文件
    try (FileOutputStream fos = new FileOutputStream(outputPath)) {
        fs.writeFilesystem(fos);
    }

    System.out.println("Document successfully encrypted");

} catch (IOException | GeneralSecurityException | InvalidFormatException e) {
    e.printStackTrace();
}

额外优化建议

  • 调大临时文件阈值:将ZipInputStreamZipEntrySource.setThresholdBytesForTempFiles设置为更大值(如10010241024),进一步减少小文件的内存占用。
  • 使用64位JVM:确保运行环境为64位Java虚拟机,提升JVM可分配内存上限,避免其他内存不足异常。
  • 清理临时文件:可在finally块中主动删除临时文件,或使用tempFile.deleteOnExit()确保JVM关闭时清理临时文件。

内容的提问来源于stack exchange,提问作者harsimranb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:02:04