读取Zip包内XLSX文件时出现Zip签名错误的解决求助
问题分析
你遇到的ZipException: Cannot find zip signature within the first 4096 bytes错误,核心原因是:当你通过zipInputStream.getNextEntry()定位到XLSX条目后,当前的zipInputStream指针已经指向了该条目的内容起始位置,而XLSX本身就是一种Zip格式文件,XSSFWorkbook会尝试把传入的流当作完整的Zip包解析,但此时流的开头并不是Zip包的签名(而是XLSX条目内容的开头),所以报错找不到Zip签名。
解决方案
需要将ZipEntry对应的XLSX内容完整读取到一个独立的输入流中,再传给XSSFWorkbook。推荐使用ByteArrayInputStream来包裹读取到的字节数据,确保XSSFWorkbook拿到的是完整的、从头开始的XLSX内容流。
同时,建议用try-with-resources语法自动管理流和Workbook的资源,避免手动关闭遗漏导致的泄漏。
修正后的代码示例:
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.web.multipart.MultipartFile; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Objects; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; // 导入Apache Commons IO的IOUtils,或者自己实现字节读取逻辑 import org.apache.commons.io.IOUtils; for(MultipartFile file : files) { if (Objects.requireNonNull(file.getContentType()).contains("application/x-zip-compressed")) { try (InputStream fileInputStream = file.getInputStream(); ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) { ZipEntry entry; while ((entry = zipInputStream.getNextEntry()) != null) { if (entry.getName().endsWith(".xlsx") && !entry.isDirectory()) { // 将当前ZipEntry的内容读取为字节数组 byte[] xlsxBytes = IOUtils.toByteArray(zipInputStream); // 用ByteArrayInputStream包裹,确保流从头开始 try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xlsxBytes); XSSFWorkbook newWorkbook = new XSSFWorkbook(byteArrayInputStream)) { Sheet newSheet = newWorkbook.getSheetAt(0); // 在这里读取Sheet内容 // ... } } // 关闭当前ZipEntry zipInputStream.closeEntry(); } } catch (IOException e) { // 处理IO异常 e.printStackTrace(); } } }
关键注意事项
- 必须跳过Zip中的目录条目:添加
!entry.isDirectory()判断,避免尝试读取目录内容导致错误。 - 不要直接复用
ZipInputStream给XSSFWorkbook:ZipInputStream在定位到条目后,指针已经移动,无法满足XSSFWorkbook对完整Zip流的要求。 - 依赖
Apache Commons IO简化字节读取:如果不想引入该依赖,可以手动实现字节读取逻辑(比如用ByteArrayOutputStream循环读取字节)。
内容的提问来源于stack exchange,提问作者chaunv
相关产品推荐
相关产品推荐

