利用Apache的POI包sax解析大数据量的Excel2007版本以上数据(Java实现,完全解析excel思路)

首先说明,本文章引用了apache官网上的代码,本文章只细讲解利用POI包SAX解析版本为excel2007以上版本的数据。

我们知道excel2007以上版本采用xml存储格式,我们可以直接修改一个后缀.xlsx为.zip,然后解压文件,我们可以看到很多文件夹,文件夹下都是xml格式的文件。至此,我们只要稍稍细心的观察一下里面的结构,就可以有了解析的思路了。

(xml文件夹结构)

打开其中一个:

大概观察一下里面的结构,可以根据row元素,c元素和v元素获取表格里面的值。

再来看看apache的POI对excel操作的支持(如下图,图片引至apache官网),因为我们解析的excel是2007+版本的数据,又不用解析图片,更不会写excel,所以我们完全可以可以用POI里面的sax解析。

废话不多说,先贴一段apache官网上面的示例。以下是源码,修改了类名,读取excel的参数,采用的是jdk自带的sax解析
package com.it.test;

import java.io.InputStream;
import java.util.Iterator;

import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

public class PoiSaxTest {
	public void processOneSheet(String filename) throws Exception {
		OPCPackage pkg = OPCPackage.open(filename);
		XSSFReader r = new XSSFReader( pkg );
		SharedStringsTable sst = r.getSharedStringsTable();

		XMLReader parser = fetchSheetParser(sst);

		// rId2 found by processing the Workbook
		// Seems to either be rId# or rSheet#
		InputStream sheet2 = r.getSheet("rId2");
		InputSource sheetSource = new InputSource(sheet2);
		parser.parse(sheetSource);
		sheet2.close();
	}

	public void processAllSheets(String filename) throws Exception {
		OPCPackage pkg = OPCPackage.open(filename);
		XSSFReader r = new XSSFReader( pkg );
		SharedStringsTable sst = r.getSharedStringsTable();
		
		XMLReader parser = fetchSheetParser(sst);

		Iterator sheets = r.getSheetsData();
		while(sheets.hasNext()) {
			System.out.println("Processing new sheet:\n");
			InputStream sheet = sheets.next();
			InputSource sheetSource = new InputSource(sheet);
			parser.parse(sheetSource);
			sheet.close();
			System.out.println("");
		}
	}

	public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
		/* 如果想使用此sax解析,需要下载对应的jar包
		 * 
		XMLReader parser =
			XMLReaderFactory.createXMLReader(
					"org.apache.xerces.parsers.SAXParser"
			);
		 */
		//这里使用jdk自带的sax解析包
		XMLReader parser = XMLReaderFactory.createXMLReader(); 
		ContentHandler handler = new SheetHandler(sst);
		parser.setContentHandler(handler);
		return parser;
	}

	/** 
	 * See org.xml.sax.helpers.DefaultHandler javadocs 
	 */
	private static class SheetHandler extends DefaultHandler {
		private SharedStringsTable sst;
		private String lastContents;
		private boolean nextIsString;
		
		private SheetHandler(SharedStringsTable sst) {
			this.sst = sst;
		}
		
		public void startElement(String uri, String localName, String name,
				Attributes attributes) throws SAXException {
			// c => cell
			if(name.equals("c")) {
				// Print the cell reference
				System.out.print(attributes.getValue("r") + " - ");
				// Figure out if the value is an index in the SST
				String cellType = attributes.getValue("t");
				if(cellType != null && cellType.equals("s")) {
					nextIsString = true;
				} else {
					nextIsString = false;
				}
			}
			// Clear contents cache
			lastContents = "";
		}
		
		public void endElement(String uri, String localName, String name)
				throws SAXException {
			// Process the last contents as required.
			// Do now, as characters() may be called more than once
			if(nextIsString) {
				int idx = Integer.parseInt(lastContents);
				lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
				nextIsString = false;
			}
			// v => contents of a cell
			// Output after we've seen the string contents
			if(name.equals("v")) {
				System.out.println(lastContents);
			}
		}

		public void characters(char[] ch, int start, int length)
				throws SAXException {
			lastContents += new String(ch, start, length);
		}
	}
	
	public static void main(String[] args) throws Exception {
		PoiSaxTest example = new PoiSaxTest();
		String filepath = "F:\\poitest\\test.xlsx";
		example.processOneSheet(filepath);
		example.processAllSheets(filepath);
	}
}

里面有很多注释都是没有修改的,源码分析:如果用过sax解析xml的技术员大概就能理解了,主要是继承了sax的DefaultHandler方法,重写了一些方法(startElement,endElement)而已,然后对其中xml进行了筛选获取,想要获取其他的元素,我们只要看看excel存储的xml格式就可以利用原理简单获取了。

讲到这里,其他很多类似文章都结束了,但是为了我们能全面的解析excel文件,我们继续对其做一些研究。既然excel保存为了xml格式的,理论上所有的信息我们都可以解析出来,举一个简单的例子。我们想要获取sheet的名称该怎么获取,我去看了sheet(索引).xml中,并没有sheet的名字,但是却在workshoop.xml中看到了sheet的名称,而且还对应的有sheetId。如图所示:


这个时候我们只需要把workbook.xml的文件流解析一下就可以了,我们试着加入以下代码:

		// 解析workbook
		InputStream wkinput = r.getWorkbookData();
		InputSource wkis = new InputSource(wkinput);
		parser.parse(wkis);

然后在startElement中加入下面代码就可以打印出所有的sheet名称了。

			if(name.equals("sheet")){
				String sheetname = attributes.getValue("name");
				System.out.println("sheetname : "+sheetname);
			}

打印结果:

sheetname : 测试数据
sheetname : 这是一个新sheet
sheetname : Sheet3

然后可以把sheet的id和name信息用map保存起来,想什么时候用就什么时候取就是了。我们甚至可以利用这个功能解析excel的公式,样式,主题等一系列信息。

我们也可以现有的功能进行扩展,例如传入sheet名称和excel路劲解析对应的sheet功能;传入excel路径和一个sheet名称数组,解析制定的多张sheet表;有了这些思路我们都可以做。

因为最近项目组需要,所以简单的对这个POI解析excel文件进行了研究,本人技术尚浅,难免有纰漏之处,还请见谅。这些都是手敲的,代码都是经过自己测试了的,希望能对有这方面需求的同仁有所帮助。如果各位觉得有什么不对的或者是有什么指教的,请留言,谢谢!





你可能感兴趣的:(Java开发)