【Java基础】 使用POI解析excel时格式判定问题及解决方案

 写在前面

        本文主要介绍在实际开发过程中使用POI工具类去解析Excel格式文件遇到的问题引发的思考、学习以及解决方案,仅供参考,有考虑不周的地方还请指正。

问题描述

         博主在做excel解析的时候,遇到了一个奇怪的现象——.xlsx拓展名的文件使用POI工具类的XSSFWorkbook进行程序初始化时候报格式错误,而使用HSSFWorkbook时候运行正常。

        而当博主在新建一个.xlsx格式文件并将原文件中的数据复制到新的workbook中,再去使用XSSFWorkbook解析便可以正常执行。是什么原因导致在视觉上一致的文件,在使用POI解析时候有那么大的差别呢?

思考

POI很多网上给出的使用方法中说明XSSFWorkbook与HSSFWorkbook使用差别:

XSSF用于解析Excel2007版本开始,扩展名为.xlsx的excel文件。

HSSF用于解析Excel97-2003版本,扩展名为.xls的excel文件。

思考一下,若其只是按照拓展名来判定excel文件的格式,会出现这种情况吗?

很明显并不会,情况发生了,便只有一种解释——看着是.xlsx格式的文件本质上是一个.xls格式文件。

问题找到了,就是拓展名与POI解析出来的文件格式不一致,下一步便是去了解POI如何判断Excel文件的格式。

POI对Excel文件的判定标准

博主去官网查看了XSSFWorkbook的源码,在其构造函数中可以看到调用了

  源码路径:poi/XSSFWorkbook.java at trunk · apache/poi · GitHub

 继续追溯PackageHelper的open(InputStream stream, boolean closeStream)方法

 源码路径:poi/PackageHelper.java at b52143528ac2f7eab4bd63bc64f4f957d7bb2f31 · apache/poi · GitHub

继续追溯OPCPackage的open(InputStream in)方法

 

 源码路径:poi/OPCPackage.java at b52143528ac2f7eab4bd63bc64f4f957d7bb2f31 · apache/poi · GitHub

 继续查看并找到对应的ZipPackage构造方法

 源码路径:poi/ZipPackage.java at b52143528ac2f7eab4bd63bc64f4f957d7bb2f31 · apache/poi · GitHub

 查看ZipHelper的openZipStream(InputStream stream)方法,可以看到想看的值主要跟两个哈部署有关,一个是FileMagic的prepareToCheckMagic方法,一个是本类的verifyZipHeader方法

 

 源码路径:poi/ZipHelper.java at b52143528ac2f7eab4bd63bc64f4f957d7bb2f31 · apache/poi · GitHub

我们先查看FileMagic的prepareToCheckMagic方法

 这边可以看到仅仅是一个准备的方法,并没有判断

 回到ZipHelper类,查看verifyZipHeader方法,在verifyZipHeader方法中对FileMagic.valueOf(is);的返回值进行了半段,从抛出的异常中可以看出若是OLE2便会抛出OLE2NotOfficeXmlFileException,说明格式不正确,应该使用HSSF解析,XSSF对应解析格式应该为OOXML,而且对于返回为XML则会报版本过低的异常(2003版本)。

下面便是看一下FileMagic类的valueOf方法返回值依赖于什么数据的。

 

源码路径:poi/FileMagic.java at b52143528ac2f7eab4bd63bc64f4f957d7bb2f31 · apache/poi · GitHub

 可以看到是获取字节流的第一个字节去判断,而非通过后缀名的格式取判断。

解决方案

问题找到了,如何取解决呢?博主是使用POI自带的判断字节流头部判断方法取处理,声明一个私有全局变量Type去标记当前文件的格式,以便后续的使用,后续的逻辑就很简单了,每个功能写两遍同名不同参的方法就行(XSSF与HSSF)。

public class TabsExcel {
	static final Logger logger = Logger.getLogger(TabsExcel.class);

	private Workbook workbook = null;

	private String Type = "";

	public TabsExcel() {
		InputStream inp;
		try {
			inp = new FileInputStream(
					System.getProperty("user.dir") + File.separator + "input" + File.separator + "文件名.xlsx");
			BufferedInputStream bis = new BufferedInputStream(inp);
			if (POIFSFileSystem.hasPOIFSHeader(bis)) {
				workbook = new HSSFWorkbook(bis);
				Type = "XLS";
			} else if (POIXMLDocument.hasOOXMLHeader(bis)) {
				workbook = new XSSFWorkbook(OPCPackage.open(bis));
				Type = "XLSX";
			}
		} catch (FileNotFoundException e) {
			logger.info("there something wrong,please check the log");
			logger.error("Excel not found : No Excel to be parsed was found, please check the Input folder!");
			logger.error(e.getMessage(), e);
			System.exit(0);
		} catch (IOException e) {
			logger.info("there is something wrong, please check the log");
			logger.error(e.getMessage(), e);
		} catch (InvalidFormatException e) {
			logger.info("there something wrong,please check the log");
			logger.error(e.getMessage(), e);
		}
	}
}

代码中使用了log4j作为log的输出,需要的小伙伴可以自行下载配置文件,不需要的只要将log对应的代码删除就可以。

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
以下是一个使用Apache POI解析Excel文件的Java示例: ```java import java.io.File; import java.io.FileInputStream; import java.io.IOException; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; public class ExcelParser { public static void main(String[] args) { try { //打开Excel文件 FileInputStream file = new FileInputStream(new File("example.xlsx")); Workbook workbook = WorkbookFactory.create(file); //获取第一个工作表 Sheet sheet = workbook.getSheetAt(0); //迭代每一行 for (Row row : sheet) { //迭代每一列 for (Cell cell : row) { //根据单元格类型打印内容 switch (cell.getCellType()) { case STRING: System.out.print(cell.getStringCellValue() + "\t"); break; case NUMERIC: System.out.print(cell.getNumericCellValue() + "\t"); break; case BOOLEAN: System.out.print(cell.getBooleanCellValue() + "\t"); break; default: System.out.print("\t"); } } System.out.println(); } //关闭文件 file.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` 在这个例子中,我们首先打开一个Excel文件,然后获取第一个工作表。接下来,我们迭代每一行和每一列,并根据单元格类型打印出每个单元格的内容。最后,我们关闭文件。 这只是一个简单的示例,实际上您可以使用POI库读取和写入Excel文件的各个部分,包括单元格样式和公式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲸海鹿林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值