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

Apache POI读取第三方Excel报ZipEntry异常 求自动修复方案

Apache POI读取Excel文件报错:Failed to read zip entry source

报错信息

java.io.IOException: Failed to read zip entry source
at org.apache.poi.openxml4j.opc.ZipPackage.(ZipPackage.java:103)
at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:324)
at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:185)
at org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:144)
at com.apsec.groovy.job.util.POIUtil.openWorkbook(POIUtil.groovy:60)
at com.apsec.groovy.job.axiom.AxiomAToMReportV2Load.run(AxiomAToMReportV2Load.groovy:213)
at com.apsec.groovy.job.AbstractJob.run(AbstractJob.groovy:67)
at com.apsec.groovy.job.developer.LocalEnvironment.run(LocalEnvironment.groovy:106)
at com.apsec.groovy.job.axiom.AxiomAToMReportV2Load.main(AxiomAToMReportV2Load.groovy:359)
Caused by: java.util.zip.ZipException: invalid entry size (expected 0 but got 1053 bytes)
at java.util.zip.ZipInputStream.readEnd(ZipInputStream.java:384)
at java.util.zip.ZipInputStream.read(ZipInputStream.java:196)
at org.apache.poi.openxml4j.util.ZipSecureFile$ThresholdInputStream.read(ZipSecureFile.java:219)
at java.io.FilterInputStream.read(FilterInputStream.java:107)
at org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource$FakeZipEntry.(ZipInputStreamZipEntrySource.java:132)
at org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource.(ZipInputStreamZipEntrySource.java:56)
at org.apache.poi.openxml4j.opc.ZipPackage.(ZipPackage.java:100)
... 8 more

环境与现有代码

  • Apache POI版本:3.16
  • 当前尝试的修复方法代码:
import org.apache.poi.ss.usermodel.Workbook
import org.apache.poi.ss.usermodel.WorkbookFactory
import org.apache.poi.openxml4j.util.ZipSecureFile
import org.apache.poi.openxml4j.exceptions.InvalidFormatException
import groovy.util.logging.Slf4j
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.FileNotFoundException

@Slf4j
static Workbook openAndRepairWorkbook(File file) throws FileNotFoundException, IOException, InvalidFormatException {
    FileInputStream fis = null
    Workbook workbook = null
    File repairedFile = new File(file.getParent(), "repaired_" + file.getName())

    try {
        if (!file.exists() || !file.canRead()) {
            log.error("File does not exist or cannot be read: {}", file.getAbsolutePath())
            throw new FileNotFoundException("File does not exist or cannot be read: " + file.getAbsolutePath())
        }

        log.info("Attempting to open workbook from file: {}", file.getAbsolutePath())
        fis = new FileInputStream(file)
        ZipSecureFile.setMinInflateRatio(0.0001) // Adjust the zip bomb detection threshold

        try {
            workbook = WorkbookFactory.create(fis)
            log.info("Workbook successfully opened.")

            // Save the workbook to a new file to "repair" it
            log.info("Saving workbook to repaired file: {}", repairedFile.getAbsolutePath())
            FileOutputStream fos = new FileOutputStream(repairedFile)
            workbook.write(fos)
            fos.close()
            workbook.close()

            // Reopen the repaired file
            log.info("Reopening the repaired workbook.")
            fis = new FileInputStream(repairedFile)
            workbook = WorkbookFactory.create(fis)
            log.info("Repaired workbook successfully opened.")
        } catch (InvalidFormatException e) {
            log.error("InvalidFormatException encountered: {}", e.getMessage())
            throw e
        } catch (IOException e) {
            log.error("IOException encountered: {}", e.getMessage())
            throw e
        }
    } finally {
        if (fis != null) {
            try {
                fis.close()
            } catch (IOException e) {
                log.error("Error closing FileInputStream: {}", e.getMessage())
            }
        }
    }
    return workbook
}

已尝试的方案

  • 手动修复:用Microsoft Excel打开问题文件并直接保存,文件即可被代码正常处理;
  • 代码模拟修复:通过上述方法尝试打开后保存再重新读取,但仍报相同错误。

需求

  1. 定位该zip entry source相关IOException的根因;
  2. 实现可自动打开、修复并处理文件的代码方案。

解决建议

根因分析

报错核心是java.util.zip.ZipException: invalid entry size (expected 0 but got 1053 bytes),说明Excel文件(本质是ZIP包)内存在一个条目,其ZIP头声明的大小为0,但实际读取到1053字节。这是第三方生成Excel时的格式不规范导致:可能是写入ZIP条目时未正确更新头信息,或文件生成过程中出现截断、损坏,造成ZIP结构不一致。

你的代码修复方案无效,是因为POI在初始读取时就因ZIP结构错误抛出异常,根本没成功打开原文件,无法执行后续保存修复步骤。

代码修复方案

方案1:先修复ZIP结构再读取

借助Java原生ZIP工具,重新写入ZIP条目以修正结构错误:

@Slf4j
static File repairZipFile(File damagedFile) throws IOException {
    File tempRepairedZip = new File(damagedFile.getParent(), "temp_repaired_" + damagedFile.getName())
    ZipInputStream zis = new ZipInputStream(new FileInputStream(damagedFile))
    ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tempRepairedZip))

    ZipEntry entry
    while ((entry = zis.getNextEntry()) != null) {
        try {
            // 创建新条目,忽略原条目的错误大小声明
            ZipEntry newEntry = new ZipEntry(entry.getName())
            zos.putNextEntry(newEntry)
            
            byte[] buffer = new byte[1024]
            int len
            while ((len = zis.read(buffer)) > 0) {
                zos.write(buffer, 0, len)
            }
            zos.closeEntry()
        } catch (ZipException e) {
            log.warn("跳过损坏的ZIP条目: {}", entry.getName(), e)
            continue
        }
    }
    zis.close()
    zos.close()
    return tempRepairedZip
}

static Workbook openAndRepairWorkbook(File file) throws FileNotFoundException, IOException, InvalidFormatException {
    if (!file.exists() || !file.canRead()) {
        log.error("文件不存在或无法读取: {}", file.getAbsolutePath())
        throw new FileNotFoundException("文件不存在或无法读取: " + file.getAbsolutePath())
    }

    // 先修复ZIP结构
    File tempZip = repairZipFile(file)
    try {
        ZipSecureFile.setMinInflateRatio(0.0001)
        Workbook workbook = WorkbookFactory.create(new FileInputStream(tempZip))
        
        // 保存为标准Excel文件
        File finalRepairedFile = new File(file.getParent(), "repaired_" + file.getName())
        FileOutputStream fos = new FileOutputStream(finalRepairedFile)
        workbook.write(fos)
        fos.close()
        
        // 删除临时修复文件
        tempZip.delete()
        
        return workbook
    } finally {
        // 确保临时文件被清理
        if (tempZip.exists()) {
            tempZip.delete()
        }
    }
}

方案2:升级Apache POI版本

POI 3.16版本较老,后续版本(如4.1.2及以上)对损坏ZIP结构的兼容性更强,可能直接处理这类不规范文件。若条件允许,升级版本是更简便的方案。

方案3:使用OPCPackage手动修复

绕过WorkbookFactory,直接调用OPCPackage的修复逻辑:

import org.apache.poi.openxml4j.opc.OPCPackage
import org.apache.poi.openxml4j.opc.PackageAccess

@Slf4j
static Workbook openDamagedWorkbook(File file) throws IOException, InvalidFormatException {
    OPCPackage pkg = OPCPackage.open(file, PackageAccess.READ_WRITE)
    // 触发POI内部修复逻辑
    pkg.repair()
    Workbook workbook = WorkbookFactory.create(pkg)
    
    File repairedFile = new File(file.getParent(), "repaired_" + file.getName())
    FileOutputStream fos = new FileOutputStream(repairedFile)
    workbook.write(fos)
    fos.close()
    pkg.close()
    
    return WorkbookFactory.create(new FileInputStream(repairedFile))
}

注意事项

  • 修复后的文件可能丢失第三方工具添加的自定义元数据,但核心数据通常能保留;
  • 临时文件需在finally块中清理,避免占用磁盘空间;
  • 升级POI版本时,需注意API兼容性,部分旧方法可能已被废弃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 09:50:55