记录一下开发中使用POI中的HSSFWorkbook来读取Excel数据时,遇到的 excel 导入出现的异常问题。

先上部分解析代码:

            //获取excel文件流
            POIFSFileSystem fileSystem = new POIFSFileSystem(myfile.getInputStream());
            HSSFWorkbook workbook = new HSSFWorkbook(fileSystem);

问题出在 POIFSFileSystem fileSystem = new POIFSFileSystem(myfile.getInputStream()); 这行,异常概要信息如下:

org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)
	at org.apache.poi.poifs.storage.HeaderBlock.<init>(HeaderBlock.java:131)
	at org.apache.poi.poifs.storage.HeaderBlock.<init>(HeaderBlock.java:104)
	at org.apache.poi.poifs.filesystem.POIFSFileSystem.<init>(POIFSFileSystem.java:138)

org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)

 

经过查询分析,可以得知,这是由于 excel 版本问题,我们使用了解析 2003版excel (.xls)的方法来解析 2007版的 excel (.xlsx)后抛出的异常。

经过查询,可以得知 HSSFWorkbook 是用来解析 03版的 excel 文件(.xls),而解析 07版的 excel 文件(.xlsx) 则需要使用 XSSFWorkbook 来读取,知道原因后,我们就可以做出改进方案了。

既然解析需要根据 excel版本 而使用不同的 对象,那我们直接根据文件后缀做个判断就好,例如:

		//获得原始文件名
        String oriName = myfile.getOriginalFilename();
		if (oriName.endsWith("xls")) {
			//使用 HSSFWorkbook 解析
		} else {
			//使用 XSSFWorkbook 解析
		}

这样就可以解决上述问题了,但总感觉这样做有些不合适,于是继续翻博客,找到大佬写的 POI - 读取Excel2003、Excel2007或更高级的兼容性问题 ,在这篇博客里和源码可以知道因为 HSSFWorkbook和XSSFWorkbook都实现了Workbook接口,所以可以使用poi-ooxml中的 WorkbookFactory.create(inputStream) 来创建Workbook。

WorkbookFactory.create() 函数中的源码如下:

/**
	 * Creates the appropriate HSSFWorkbook / XSSFWorkbook from
	 *  the given InputStream.
	 * Your input stream MUST either support mark/reset, or
	 *  be wrapped as a {@link PushbackInputStream}!
	 */
	public static Workbook create(InputStream inp) throws IOException, InvalidFormatException {
		// If clearly doesn't do mark/reset, wrap up
		if(! inp.markSupported()) {
			inp = new PushbackInputStream(inp, 8);
		}
		
		if(POIFSFileSystem.hasPOIFSHeader(inp)) {
			return new HSSFWorkbook(inp);
		}
		if(POIXMLDocument.hasOOXMLHeader(inp)) {
			return new XSSFWorkbook(OPCPackage.open(inp));
		}
		throw new IllegalArgumentException("Your InputStream was neither an OLE2 stream, nor an OOXML stream");
	}

从上述源码中可知,WorkbookFactory.create() 函数里面,对当前传入的 excel文件流进行了版本判断,并根据不同的版本,返回不同的 Workbook 对象。

我们只要使用

Workbook wb = WorkbookFactory.create(is);

就可以正确创建出 Workbook 对象,而无需判断当前 excel 文件类型,这样代码显得更为优美,符合我当前的审美和要求。根据当前返回的 Workbook 对象就可以获取 excel的 sheet 对象,从而进行获取当前 sheet 的每一行数据。

            //获取excel文件流
            Workbook workbook = WorkbookFactory.create(myfile.getInputStream());
            //获取sheet
            Sheet sheet = workbook.getSheetAt(0);

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐