资讯专栏INFORMATION COLUMN

POI Sax 事件驱动解析Excel2007文件(较大数据量的读存导出)

cod7ce / 283人阅读

摘要:方式解析全称,它是一个接口,也是一个软件包。另外,推模型可用于广播环境,能够同时注册多个,并行接收事件,而不是在一个管道中一个接一个地进行处理。这些语法分析器是最灵活的,因为它们还支持。

xcel2007版本的代码如下,本文主要是用于POI解析大文件Excel容易出现内存溢出的现象而提出解决方案,故此解决了大数据量的Excel文件解析的难度,在此拿出来贡献给大家,谢谢!

1.Office2007与Office Open XML

在Office 2007之前,Office一直都是以二进制位的方式存储,但这种格式不易被其它软件拿来使用,在各界的压力下,MicroSoft于2005年发布了基于XML的ooxml开放文档标准。ooxml的xml schema强调减少load time,增快parsing speed,将child elements分开存储,而不是multiple attributes一起存,这有点类似于HTML的结构。ooxml 使用XML和ZIP技术结合进行文件存储,因为XML是一个基于文本的格式,而且ZIP容器支持内容的压缩,所以其一大优势就是可以大大减小文件的尺寸。其它特点这里不再叙述。

2.SAX方式解析XML

SAX全称Simple API for XML,它是一个接口,也是一个软件包。它是一种XML解析的替代方法,不同于DOM解析XML文档时把所有内容一次性加载到内存中的方式,它逐行扫描文档,一边扫描,一边解析。所以那些只需要单遍读取内容的应用程序就可以从SAX解析中受益,这对大型文档的解析是个巨大优势。另外,SAX “推" 模型可用于广播环境,能够同时注册多个ContentHandler,并行接收事件,而不是在一个管道中一个接一个地进行处理。一些支持 SAX 的语法分析器包括 Xerces,Apache parser(以前的 IBM 语法分析器)、MSXML(Microsoft 语法分析器)和 XDK(Oracle 语法分析器)。这些语法分析器是最灵活的,因为它们还支持 DOM。

3.POI以SAX解析excel2007文件

所需jar包:
poi-3.10-FINAL-20140208.jar,
poi-ooxml-3.10-FINAL-20140208.jar,
poi-ooxml-schemas-3.10-FINAL-20140208.jar
xercesImpl.jar
xml-apis-2.0.2.jar
xmlbeans-2.6.0.jar
sax2.jar

package com.boguan.bte.util.excel;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.xml.sax.Attributes;
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;

import com.boguan.bte.service.common.IExcelRowReader;
import com.boguan.bte.service.common.impl.ExcelRowReader;

/**
 * 名称: ExcelXlsxReader.java
* 描述:
* 类型: JAVA
* 最近修改时间:2016年7月5日 上午10:00:52
* * @since 2016年7月5日 * @author “” */ public class ExcelXlsxReader extends DefaultHandler { private IExcelRowReader rowReader; public void setRowReader(IExcelRowReader rowReader) { this.rowReader = rowReader; } /** * 共享字符串表 */ private SharedStringsTable sst; /** * 上一次的内容 */ private String lastContents; /** * 字符串标识 */ private boolean nextIsString; /** * 工作表索引 */ private int sheetIndex = -1; /** * 行集合 */ private List rowlist = new ArrayList(); /** * 当前行 */ private int curRow = 0; /** * 当前列 */ private int curCol = 0; /** * T元素标识 */ private boolean isTElement; /** * 异常信息,如果为空则表示没有异常 */ private String exceptionMessage; /** * 单元格数据类型,默认为字符串类型 */ private CellDataType nextDataType = CellDataType.SSTINDEX; private final DataFormatter formatter = new DataFormatter(); private short formatIndex; private String formatString; // 定义前一个元素和当前元素的位置,用来计算其中空的单元格数量,如A6和A8等 private String preRef = null, ref = null; // 定义该文档一行最大的单元格数,用来补全一行最后可能缺失的单元格 private String maxRef = null; /** * 单元格 */ private StylesTable stylesTable; /** * 遍历工作簿中所有的电子表格 * * @param filename * @throws IOException * @throws OpenXML4JException * @throws SAXException * @throws Exception */ public void process(String filename) throws IOException, OpenXML4JException, SAXException { OPCPackage pkg = OPCPackage.open(filename); XSSFReader xssfReader = new XSSFReader(pkg); stylesTable = xssfReader.getStylesTable(); SharedStringsTable sst = xssfReader.getSharedStringsTable(); XMLReader parser = this.fetchSheetParser(sst); Iterator sheets = xssfReader.getSheetsData(); while (sheets.hasNext()) { curRow = 0; sheetIndex++; InputStream sheet = sheets.next(); InputSource sheetSource = new InputSource(sheet); parser.parse(sheetSource); sheet.close(); } } public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException { XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser"); this.sst = sst; parser.setContentHandler(this); return parser; } public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { // c => 单元格 if ("c".equals(name)) { // 前一个单元格的位置 if (preRef == null) { preRef = attributes.getValue("r"); } else { preRef = ref; } // 当前单元格的位置 ref = attributes.getValue("r"); // 设定单元格类型 this.setNextDataType(attributes); // 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; } } // 当元素为t时 if ("t".equals(name)) { isTElement = true; } else { isTElement = false; } // 置空 lastContents = ""; } /** * 单元格中的数据可能的数据类型 */ enum CellDataType { BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL } /** * 处理数据类型 * * @param attributes */ public void setNextDataType(Attributes attributes) { nextDataType = CellDataType.NUMBER; formatIndex = -1; formatString = null; String cellType = attributes.getValue("t"); String cellStyleStr = attributes.getValue("s"); String columData = attributes.getValue("r"); if ("b".equals(cellType)) { nextDataType = CellDataType.BOOL; } else if ("e".equals(cellType)) { nextDataType = CellDataType.ERROR; } else if ("inlineStr".equals(cellType)) { nextDataType = CellDataType.INLINESTR; } else if ("s".equals(cellType)) { nextDataType = CellDataType.SSTINDEX; } else if ("str".equals(cellType)) { nextDataType = CellDataType.FORMULA; } if (cellStyleStr != null) { int styleIndex = Integer.parseInt(cellStyleStr); XSSFCellStyle style = stylesTable.getStyleAt(styleIndex); formatIndex = style.getDataFormat(); formatString = style.getDataFormatString(); if ("m/d/yy" == formatString) { nextDataType = CellDataType.DATE; formatString = "yyyy-MM-dd hh:mm:ss.SSS"; } if (formatString == null) { nextDataType = CellDataType.NULL; formatString = BuiltinFormats.getBuiltinFormat(formatIndex); } } } /** * 对解析出来的数据进行类型处理 * * @param value * 单元格的值(这时候是一串数字) * @param thisStr * 一个空字符串 * @return */ @SuppressWarnings("deprecation") public String getDataValue(String value, String thisStr) { switch (nextDataType) { // 这几个的顺序不能随便交换,交换了很可能会导致数据错误 case BOOL: char first = value.charAt(0); thisStr = first == "0" ? "FALSE" : "TRUE"; break; case ERROR: thisStr = ""ERROR:" + value.toString() + """; break; case FORMULA: thisStr = """ + value.toString() + """; break; case INLINESTR: XSSFRichTextString rtsi = new XSSFRichTextString(value.toString()); thisStr = rtsi.toString(); rtsi = null; break; case SSTINDEX: String sstIndex = value.toString(); try { int idx = Integer.parseInt(sstIndex); XSSFRichTextString rtss = new XSSFRichTextString(sst.getEntryAt(idx)); thisStr = rtss.toString(); rtss = null; } catch (NumberFormatException ex) { thisStr = value.toString(); } break; case NUMBER: if (formatString != null) { thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString).trim(); } else { thisStr = value; } thisStr = thisStr.replace("_", "").trim(); break; case DATE: thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString); // 对日期字符串作特殊处理 thisStr = thisStr.replace(" ", "T"); break; default: thisStr = " "; break; } return thisStr; } @Override public void endElement(String uri, String localName, String name) throws SAXException { // 根据SST的索引值的到单元格的真正要存储的字符串 // 这时characters()方法可能会被调用多次 if (nextIsString && && StringUtils.isNotEmpty(lastContents) && StringUtils.isNumeric(lastContents)) { int idx = Integer.parseInt(lastContents); lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString(); } // t元素也包含字符串 if (isTElement) { // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符 String value = lastContents.trim(); rowlist.add(curCol, value); curCol++; isTElement = false; } else if ("v".equals(name)) { // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引 String value = this.getDataValue(lastContents.trim(), ""); // 补全单元格之间的空单元格 if (!ref.equals(preRef)) { int len = countNullCell(ref, preRef); for (int i = 0; i < len; i++) { rowlist.add(curCol, ""); curCol++; } } rowlist.add(curCol, value); curCol++; } else { // 如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法 if (name.equals("row")) { // 默认第一行为表头,以该行单元格数目为最大数目 if (curRow == 0) { maxRef = ref; } // 补全一行尾部可能缺失的单元格 if (maxRef != null) { int len = countNullCell(maxRef, ref); for (int i = 0; i <= len; i++) { rowlist.add(curCol, ""); curCol++; } } rowReader.getRows(sheetIndex, curRow, rowlist); rowlist.clear(); curRow++; curCol = 0; preRef = null; ref = null; } } } /** * 计算两个单元格之间的单元格数目(同一行) * * @param ref * @param preRef * @return */ public int countNullCell(String ref, String preRef) { // excel2007最大行数是1048576,最大列数是16384,最后一列列名是XFD String xfd = ref.replaceAll("d+", ""); String xfd_1 = preRef.replaceAll("d+", ""); xfd = fillChar(xfd, 3, "@", true); xfd_1 = fillChar(xfd_1, 3, "@", true); char[] letter = xfd.toCharArray(); char[] letter_1 = xfd_1.toCharArray(); int res = (letter[0] - letter_1[0]) * 26 * 26 + (letter[1] - letter_1[1]) * 26 + (letter[2] - letter_1[2]); return res - 1; } /** * 字符串的填充 * * @param str * @param len * @param let * @param isPre * @return */ String fillChar(String str, int len, char let, boolean isPre) { int len_1 = str.length(); if (len_1 < len) { if (isPre) { for (int i = 0; i < (len - len_1); i++) { str = let + str; } } else { for (int i = 0; i < (len - len_1); i++) { str = str + let; } } } return str; } @Override public void characters(char[] ch, int start, int length) throws SAXException { // 得到单元格内容的值 lastContents += new String(ch, start, length); } /** * @return the exceptionMessage */ public String getExceptionMessage() { return exceptionMessage; } public static void main(String[] args) { IExcelRowReader rowReader = new ExcelRowReader(); try { // ExcelReaderUtil.readExcel(rowReader, // "E://2016-07-04-011940a.xls"); System.out.println("**********************************************"); ExcelReaderUtil.readExcel(rowReader, "E://test.xlsx"); } catch (Exception e) { e.printStackTrace(); } } }

转自:http://www.cnblogs.com/wshsdl...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70258.html

相关文章

  • Excel大批量数据的导入和导出,如何做优化?

    摘要:并且在对的抽象中,每一行,每一个单元格都是一个对象。对支持使用官方例子需要继承,覆盖方法,每读取到一个单元格的数据则会回调次方法。概要Java对Excel的操作一般都是用POI,但是数据量大的话可能会导致频繁的FGC或OOM,这篇文章跟大家说下如果避免踩POI的坑,以及分别对于xls和xlsx文件怎么优化大批量数据的导入和导出。一次线上问题这是一次线上的问题,因为一个大数据量的Excel导出...

    Tecode 评论0 收藏0
  • java 导出 excel 最佳实践,java 大文件 excel 避免OOM(内存溢出) exce

    摘要:消费之后,多线程处理文件导出,生成文件后上传到等文件服务器。前端直接查询并且展现对应的任务执行列表,去等文件服务器下载文件即可。这客户体验不友好,而且网络传输,系统占用多种问题。拓展阅读导出最佳实践框架 产品需求 产品经理需要导出一个页面的所有的信息到 EXCEL 文件。 需求分析 对于 excel 导出,是一个很常见的需求。 最常见的解决方案就是使用 poi 直接同步导出一个 exc...

    K_B_Z 评论0 收藏0
  • 使用ApachePOI生成XLSX格式Excel文档大数据导出

    摘要:最近在做使用进行大数据量导出,现在把其整理成工具类供大家参考。版本增加了前缀为相关的类,主要用于大数据量的写入与读取。 最近在做使用POI进行大数据量导出,现在把其整理成工具类供大家参考。Apache POI 3.8版本增加了前缀为SXSSF相关的类,主要用于大数据量的写入与读取。关于ApachePOI导出Excel基本的使用我这里就不详解了,具体参考: Apache POI官方网站...

    Shihira 评论0 收藏0
  • POI如何高效导出百万级Excel数据

    摘要:阅读原文如何高效导出百万级数据在一个具有统计功能的系统中,导出功能几乎是一定的,如何导出导出的数据有多少如何高效的导出简介什么是就不用介绍了,这里主要说明不同版本下每个下的行列限制。 阅读原文:POI如何高效导出百万级Excel数据? 在一个具有统计功能的系统中,导出excel功能几乎是一定的,如何导出excel?导出的数据有多少?如何高效的导出? Excel简介什么是excel就不用...

    lemanli 评论0 收藏0
  • Java对象和Excel转换工具XXL-EXCEL

    摘要:一行代码完成对象和之间的转换。说明属性列名称四版本更新日志版本,新特性导出支持对象装换为,并且支持字节数组等多种导出方式导入支持转换为对象,并且支持文件路径等多种导入方式版本,新特性字段支持类型。 《Java对象和Excel转换工具XXL-EXCEL》 showImg(https://segmentfault.com/img/remote/1460000012470335);showI...

    mj 评论0 收藏0

发表评论

0条评论

cod7ce

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<