package io.dataease.commons.utils; import io.dataease.datasource.dto.TableFiled; import io.dataease.dto.dataset.ExcelSheetData; import io.dataease.i18n.Translator; import org.apache.commons.collections4.CollectionUtils; import org.apache.poi.openxml4j.opc.OPCPackage; 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 java.io.InputStream; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author y * @create 2018-01-18 14:28 * @desc POI读取excel有两种模式,一种是用户模式,一种是事件驱动模式 * 采用SAX事件驱动模式解决XLSX文件,可以有效解决用户模式内存溢出的问题, * 该模式是POI官方推荐的读取大数据的模式, * 在用户模式下,数据量较大,Sheet较多,或者是有很多无用的空行的情况下,容易出现内存溢出 *
* 用于解决.xlsx2007版本大数据量问题
**/
public class ExcelXlsxReader extends DefaultHandler {
/**
* 自定义获取表格某些信息
*/
public Map map = new TreeMap> data = new ArrayList<>();
public List
> getData() {
return data;
}
public void setData(List
> data) {
this.data = data;
}
public int process(InputStream inputStream) throws Exception {
OPCPackage pkg = OPCPackage.open(inputStream);
XSSFReader xssfReader = new XSSFReader(pkg);
stylesTable = xssfReader.getStylesTable();
SharedStringsTable sst = xssfReader.getSharedStringsTable();
XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
this.sst = sst;
parser.setContentHandler(this);
XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
while (sheets.hasNext()) { //遍历sheet
curRow = 1; //标记初始行为第一行
fields.clear();
data.clear();
InputStream sheet = sheets.next(); //sheets.next()和sheets.getSheetName()不能换位置,否则sheetName报错
InputSource sheetSource = new InputSource(sheet);
parser.parse(sheetSource); //解析excel的每条记录,在这个过程中startElement()、characters()、endElement()这三个函数会依次执行
ExcelSheetData excelSheetData = new ExcelSheetData();
excelSheetData.setData(new ArrayList<>(data));
excelSheetData.setExcelLable(sheets.getSheetName());
excelSheetData.setFields(new ArrayList<>(fields));
totalSheets.add(excelSheetData);
sheet.close();
}
return totalRows; //返回该excel文件的总行数,不包括首列和空行
}
/**
* 第一个执行
*
* @param uri
* @param localName
* @param name
* @param attributes
* @throws SAXException
*/
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
if(curRow>101){
return;
}
if(name.equalsIgnoreCase("mergeCell")){
throw new RuntimeException(Translator.get("i18n_excel_have_merge_region"));
}
//c => 单元格
if ("c".equals(name)) {
//当前单元格的位置
ref = attributes.getValue("r");
//设定单元格类型
this.setNextDataType(attributes);
}
//当元素为t时
if ("t".equals(name)) {
isTElement = true;
} else {
isTElement = false;
}
//置空
lastIndex = "";
}
/**
* 第二个执行
* 得到单元格对应的索引值或是内容值
* 如果单元格类型是字符串、INLINESTR、数字、日期,lastIndex则是索引值
* 如果单元格类型是布尔值、错误、公式,lastIndex则是内容值
* @param ch
* @param start
* @param length
* @throws SAXException
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if(curRow>101){
return;
}
lastIndex += new String(ch, start, length);
}
/**
* 第三个执行
*
* @param uri
* @param localName
* @param name
* @throws SAXException
*/
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
if(curRow>101){
return;
}
//t元素也包含字符串
if (isTElement) {//这个程序没经过
//将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
String value = lastIndex.trim();
cellList.add(curCol, value);
curCol++;
isTElement = false;
//如果里面某个单元格含有值,则标识该行不为空行
if (value != null && !"".equals(value)) {
flag = true;
}
} else if ("v".equals(name)) {
//v => 单元格的值,如果单元格是字符串,则v标签的值为该字符串在SST中的索引
String value = this.getDataValue(lastIndex.trim(), "");//根据索引值获取对应的单元格值
if (preRef == null) {
String regEx="[^0-9]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(ref);
if(curCol < Integer.valueOf(m.replaceAll("").trim()) -1 ){
cellList.add(curCol, "");
curCol++;
}
preRef = ref;
}
//补全单元格之间的空单元格
if (!"A".equals(preRef.substring(0, 1)) && curRow==1 && preRef.equalsIgnoreCase(ref)) {
throw new RuntimeException(Translator.get("i18n_excel_empty_column"));
}else if (!ref.equals(preRef)) {
int len = countNullCell(ref, preRef);
for (int i = 0; i < len; i++) {
if(curCol < this.fields.size()){
cellList.add(curCol, "");
curCol++;
}
}
}
if(curCol < this.fields.size()){
cellList.add(curCol, value);
}
curCol++;
//如果里面某个单元格含有值,则标识该行不为空行
if (value != null && !"".equals(value)) {
flag = true;
}
preRef = ref;
} else {
//如果标签名称为row,这说明已到行尾
if ("row".equals(name)) {
//默认第一行为表头,以该行单元格数目为最大数目
if (curRow == 1) {
maxRef = ref;
}
//补全一行尾部可能缺失的单元格
if (maxRef != null) {
int len = countNullCell(maxRef, ref);
for (int i = 0; i <= len; i++) {
cellList.add(curCol, "");
curCol++;
}
}
if(curRow>1){
List