add sheetId support

This commit is contained in:
Looly 2020-09-30 00:49:24 +08:00
parent a508533f9c
commit 60efd24469
9 changed files with 324 additions and 173 deletions

View File

@ -31,6 +31,7 @@
* 【core 】 NumberUtil.factorial注释明确pr#1126@Github
* 【core 】 NumberUtil增加isPowerOfTwo方法pr#1132@Github
* 【core 】 优化BooleanUtil的校验逻辑pr#1137@Github
* 【poi 】 改进sax方式读取逻辑支持sheetIdissue#1141@Github
### Bug修复
* 【crypto 】 修复SM2验签后无法解密问题issue#I1W0VP@Gitee

View File

@ -1,9 +1,10 @@
package cn.hutool.poi.excel;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import org.apache.poi.poifs.filesystem.FileMagic;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@ -48,11 +49,24 @@ public class ExcelFileUtil {
* @return 是否为XLSX格式的Excel文件XSSF
*/
public static boolean isXlsx(InputStream in) {
if (false == in.markSupported()) {
in = new BufferedInputStream(in);
}
try {
return FileMagic.valueOf(in) == FileMagic.OOXML;
return FileMagic.valueOf(IoUtil.toMarkSupportStream(in)) == FileMagic.OOXML;
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 是否为XLSX格式的Excel文件XSSF<br>
* XLSX文件主要用于Excel 2007+创建
*
* @param file excel文件
* @return 是否为XLSX格式的Excel文件XSSF
* @since 5.4.4
*/
public static boolean isXlsx(File file) {
try {
return FileMagic.valueOf(file) == FileMagic.OOXML;
} catch (IOException e) {
throw new IORuntimeException(e);
}

View File

@ -12,7 +12,6 @@ import cn.hutool.poi.excel.sax.Excel03SaxReader;
import cn.hutool.poi.excel.sax.Excel07SaxReader;
import cn.hutool.poi.excel.sax.handler.RowHandler;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.InputStream;
@ -34,13 +33,7 @@ public class ExcelUtil {
* @since 3.2.0
*/
public static void readBySax(String path, int sheetIndex, RowHandler rowHandler) {
BufferedInputStream in = null;
try {
in = FileUtil.getInputStream(path);
readBySax(in, sheetIndex, rowHandler);
} finally {
IoUtil.close(in);
}
readBySax(FileUtil.file(path), sheetIndex, rowHandler);
}
/**
@ -52,12 +45,10 @@ public class ExcelUtil {
* @since 3.2.0
*/
public static void readBySax(File file, int sheetIndex, RowHandler rowHandler) {
BufferedInputStream in = null;
try {
in = FileUtil.getInputStream(file);
readBySax(in, sheetIndex, rowHandler);
} finally {
IoUtil.close(in);
if (ExcelFileUtil.isXlsx(file)) {
read07BySax(file, sheetIndex, rowHandler);
} else {
read03BySax(file, sheetIndex, rowHandler);
}
}

View File

@ -1,38 +0,0 @@
package cn.hutool.poi.excel.sax;
import java.io.File;
import java.io.InputStream;
import cn.hutool.core.io.FileUtil;
import cn.hutool.poi.exceptions.POIException;
/**
* 抽象的Sax方式Excel读取器提供一些共用方法
*
* @author looly
*
* @param <T> 子对象类型用于标记返回值this
* @since 3.2.0
*/
public abstract class AbstractExcelSaxReader<T> implements ExcelSaxReader<T> {
@Override
public T read(String path) throws POIException {
return read(FileUtil.file(path));
}
@Override
public T read(File file) throws POIException {
return read(file, -1);
}
@Override
public T read(InputStream in) throws POIException {
return read(in, -1);
}
@Override
public T read(String path, int sheetIndex) throws POIException {
return read(FileUtil.file(path), sheetIndex);
}
}

View File

@ -30,7 +30,7 @@ import java.util.List;
*
* @author looly
*/
public class Excel03SaxReader extends AbstractExcelSaxReader<Excel03SaxReader> implements HSSFListener {
public class Excel03SaxReader implements HSSFListener, ExcelSaxReader<Excel03SaxReader> {
/**
* 如果为公式true表示输出公式计算后的结果值false表示输出公式本身
@ -83,18 +83,18 @@ public class Excel03SaxReader extends AbstractExcelSaxReader<Excel03SaxReader> i
// ------------------------------------------------------------------------------ Read start
@Override
public Excel03SaxReader read(File file, int rid) throws POIException {
public Excel03SaxReader read(File file, String idOrRid) throws POIException {
try {
return read(new POIFSFileSystem(file), rid);
return read(new POIFSFileSystem(file), idOrRid);
} catch (IOException e) {
throw new POIException(e);
}
}
@Override
public Excel03SaxReader read(InputStream excelStream, int rid) throws POIException {
public Excel03SaxReader read(InputStream excelStream, String idOrRid) throws POIException {
try {
return read(new POIFSFileSystem(excelStream), rid);
return read(new POIFSFileSystem(excelStream), idOrRid);
} catch (IOException e) {
throw new POIException(e);
}
@ -104,12 +104,12 @@ public class Excel03SaxReader extends AbstractExcelSaxReader<Excel03SaxReader> i
* 读取
*
* @param fs {@link POIFSFileSystem}
* @param rid sheet序号
* @param id sheet序号
* @return this
* @throws POIException IO异常包装
*/
public Excel03SaxReader read(POIFSFileSystem fs, int rid) throws POIException {
this.rid = rid;
public Excel03SaxReader read(POIFSFileSystem fs, String id) throws POIException {
this.rid = Integer.parseInt(id);
formatListener = new FormatTrackingHSSFListener(new MissingRecordAwareHSSFListener(this));
final HSSFRequest request = new HSSFRequest();

View File

@ -1,10 +1,14 @@
package cn.hutool.poi.excel.sax;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.sax.handler.RowHandler;
import cn.hutool.poi.exceptions.POIException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
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.xssf.eventusermodel.XSSFReader;
@ -12,10 +16,10 @@ import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
@ -28,7 +32,7 @@ import java.util.List;
* @author Looly
* @since 3.1.2
*/
public class Excel07SaxReader extends AbstractExcelSaxReader<Excel07SaxReader> implements ContentHandler {
public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<Excel07SaxReader> {
// sheet r:Id前缀
private static final String RID_PREFIX = "rId";
@ -92,20 +96,30 @@ public class Excel07SaxReader extends AbstractExcelSaxReader<Excel07SaxReader> i
// ------------------------------------------------------------------------------ Read start
@Override
public Excel07SaxReader read(File file, int rid) throws POIException {
return read(file, RID_PREFIX + rid);
}
@Override
public Excel07SaxReader read(File file, String idOrRid) throws POIException {
try {
return read(OPCPackage.open(file), rid);
} catch (Exception e) {
return read(OPCPackage.open(file), idOrRid);
} catch (InvalidFormatException e) {
throw new POIException(e);
}
}
@Override
public Excel07SaxReader read(InputStream in, int rid) throws POIException {
try {
return read(OPCPackage.open(in), rid);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
return read(in, RID_PREFIX + rid);
}
@Override
public Excel07SaxReader read(InputStream in, String idOrRid) throws POIException {
try (final OPCPackage opcPackage = OPCPackage.open(in)) {
return read(opcPackage, idOrRid);
} catch (IOException e) {
throw new IORuntimeException(e);
} catch (InvalidFormatException e) {
throw new POIException(e);
}
}
@ -113,53 +127,60 @@ public class Excel07SaxReader extends AbstractExcelSaxReader<Excel07SaxReader> i
/**
* 开始读取ExcelSheet编号从0开始计数
*
* @param opcPackage {@link OPCPackage}Excel包
* @param opcPackage {@link OPCPackage}Excel包读取后不关闭
* @param rid Excel中的sheet rid编号如果为-1处理所有编号的sheet
* @return this
* @throws POIException POI异常
*/
public Excel07SaxReader read(OPCPackage opcPackage, int rid) throws POIException {
InputStream sheetInputStream = null;
return read(opcPackage, RID_PREFIX + rid);
}
/**
* 开始读取ExcelSheet编号从0开始计数
*
* @param opcPackage {@link OPCPackage}Excel包读取后不关闭
* @param idOrRid Excel中的sheet id或者rid编号rid必须加rId前缀例如rId1如果为-1处理所有编号的sheet
* @return this
* @throws POIException POI异常
*/
public Excel07SaxReader read(OPCPackage opcPackage, String idOrRid) throws POIException {
try {
final XSSFReader xssfReader = new XSSFReader(opcPackage);
// 获取共享样式表
try {
stylesTable = xssfReader.getStylesTable();
} catch (Exception e) {
//ignore
}
// 获取共享字符串表
this.sharedStringsTable = xssfReader.getSharedStringsTable();
if (rid > -1) {
this.sheetIndex = rid;
// 根据 rId# rSheet# 查找sheet
sheetInputStream = xssfReader.getSheet(RID_PREFIX + (rid + 1));
ExcelSaxUtil.readFrom(sheetInputStream, this);
rowHandler.doAfterAllAnalysed();
} else {
this.sheetIndex = -1;
// 遍历所有sheet
final Iterator<InputStream> sheetInputStreams = xssfReader.getSheetsData();
while (sheetInputStreams.hasNext()) {
// 重新读取一个sheet时行归零
index = 0;
this.sheetIndex++;
sheetInputStream = sheetInputStreams.next();
ExcelSaxUtil.readFrom(sheetInputStream, this);
rowHandler.doAfterAllAnalysed();
}
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
return read(new XSSFReader(opcPackage), idOrRid);
} catch (OpenXML4JException e) {
throw new POIException(e);
} finally {
IoUtil.close(sheetInputStream);
IoUtil.close(opcPackage);
} catch (IOException e) {
throw new IORuntimeException(e);
}
return this;
}
/**
* 开始读取ExcelSheet编号从0开始计数
*
* @param xssfReader {@link XSSFReader}Excel读取器
* @param idOrRid Excel中的sheet id或者rid编号rid必须加rId前缀例如rId1如果为-1处理所有编号的sheet
* @return this
* @throws POIException POI异常
* @since 5.4.4
*/
public Excel07SaxReader read(XSSFReader xssfReader, String idOrRid) throws POIException {
// 获取共享样式表
try {
stylesTable = xssfReader.getStylesTable();
} catch (Exception e) {
//ignore
}
// 获取共享字符串表
try {
this.sharedStringsTable = xssfReader.getSharedStringsTable();
} catch (IOException e) {
throw new IORuntimeException(e);
} catch (InvalidFormatException e) {
throw new POIException(e);
}
return readSheets(xssfReader, idOrRid);
}
// ------------------------------------------------------------------------------ Read end
@ -196,53 +217,57 @@ public class Excel07SaxReader extends AbstractExcelSaxReader<Excel07SaxReader> i
lastContent.append(ch, start, length);
}
// --------------------------------------------------------------------------------------- Pass method start
@Override
public void setDocumentLocator(Locator locator) {
// pass
}
// --------------------------------------------------------------------------------------- Private method start
/**
* ?xml标签的回调处理方法
* 开始读取ExcelSheet编号从0开始计数
*
* @param xssfReader {@link XSSFReader}Excel读取器
* @param idOrRid Excel中的sheet id或者rid编号rid必须加rId前缀例如rId0如果为-1处理所有编号的sheet
* @return this
* @throws POIException POI异常
* @since 5.4.4
*/
@Override
public void startDocument() {
// pass
private Excel07SaxReader readSheets(XSSFReader xssfReader, String idOrRid) throws POIException {
// 将sheetId转换为rid
if (NumberUtil.isInteger(idOrRid)) {
final SheetRidReader ridReader = new SheetRidReader();
final String rid = ridReader.read(xssfReader).getRidBySheetId(idOrRid);
if (StrUtil.isNotEmpty(rid)) {
idOrRid = rid;
}
}
this.sheetIndex = Integer.parseInt(StrUtil.removePrefixIgnoreCase(idOrRid, RID_PREFIX));
InputStream sheetInputStream = null;
try {
if (this.sheetIndex > -1) {
// 根据 rId# rSheet# 查找sheet
sheetInputStream = xssfReader.getSheet(RID_PREFIX + (this.sheetIndex + 1));
ExcelSaxUtil.readFrom(sheetInputStream, this);
rowHandler.doAfterAllAnalysed();
} else {
this.sheetIndex = -1;
// 遍历所有sheet
final Iterator<InputStream> sheetInputStreams = xssfReader.getSheetsData();
while (sheetInputStreams.hasNext()) {
// 重新读取一个sheet时行归零
index = 0;
this.sheetIndex++;
sheetInputStream = sheetInputStreams.next();
ExcelSaxUtil.readFrom(sheetInputStream, this);
rowHandler.doAfterAllAnalysed();
}
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new POIException(e);
} finally {
IoUtil.close(sheetInputStream);
}
return this;
}
@Override
public void endDocument() {
// pass
}
@Override
public void startPrefixMapping(String prefix, String uri) {
// pass
}
@Override
public void endPrefixMapping(String prefix) {
// pass
}
@Override
public void ignorableWhitespace(char[] ch, int start, int length) {
// pass
}
@Override
public void processingInstruction(String target, String data) {
// pass
}
@Override
public void skippedEntity(String name) {
// pass
}
// --------------------------------------------------------------------------------------- Pass method end
// --------------------------------------------------------------------------------------- Private method start
/**
* 行开始
*
@ -317,10 +342,11 @@ public class Excel07SaxReader extends AbstractExcelSaxReader<Excel07SaxReader> i
/**
* 在一行中的指定列增加值
*
* @param index 位置
* @param value
*/
private void addCellValue(int index, Object value){
private void addCellValue(int index, Object value) {
this.rowCellList.add(index, value);
this.rowHandler.handleCell(this.sheetIndex, this.rowNumber, index, value, this.xssfCellStyle);
}

View File

@ -1,10 +1,11 @@
package cn.hutool.poi.excel.sax;
import cn.hutool.core.io.FileUtil;
import cn.hutool.poi.exceptions.POIException;
import java.io.File;
import java.io.InputStream;
import cn.hutool.poi.exceptions.POIException;
/**
* Sax方式读取Excel接口提供一些共用方法
* @author looly
@ -13,60 +14,105 @@ import cn.hutool.poi.exceptions.POIException;
* @since 3.2.0
*/
public interface ExcelSaxReader<T> {
/**
* 开始读取Excel
*
* @param file Excel文件
* @param idOrRid Excel中的sheet id或者rid编号rid必须加rId前缀例如rId1如果为-1处理所有编号的sheet
* @return this
* @throws POIException POI异常
*/
T read(File file, String idOrRid) throws POIException;
/**
* 开始读取Excel读取结束后并不关闭流
*
* @param in Excel流
* @param idOrRid Excel中的sheet id或者rid编号rid必须加rId前缀例如rId1如果为-1处理所有编号的sheet
* @return this
* @throws POIException POI异常
*/
T read(InputStream in, String idOrRid) throws POIException;
/**
* 开始读取Excel读取所有sheet
*
*
* @param path Excel文件路径
* @return this
* @throws POIException POI异常
*/
T read(String path) throws POIException;
default T read(String path) throws POIException {
return read(FileUtil.file(path));
}
/**
* 开始读取Excel读取所有sheet
*
*
* @param file Excel文件
* @return this
* @throws POIException POI异常
*/
T read(File file) throws POIException;
default T read(File file) throws POIException {
return read(file, -1);
}
/**
* 开始读取Excel读取所有sheet读取结束后并不关闭流
*
*
* @param in Excel包流
* @return this
* @throws POIException POI异常
*/
T read(InputStream in) throws POIException;
default T read(InputStream in) throws POIException {
return read(in, -1);
}
/**
* 开始读取Excel
*
*
* @param path 文件路径
* @param rid Excel中的sheet rid编号如果为-1处理所有编号的sheet
* @return this
* @throws POIException POI异常
*/
T read(String path, int rid) throws POIException;
default T read(String path, int rid) throws POIException {
return read(FileUtil.file(path), rid);
}
/**
* 开始读取Excel
*
*
* @param path 文件路径
* @param rid Excel中的sheet rid编号如果为-1处理所有编号的sheet
* @return this
* @throws POIException POI异常
*/
default T read(String path, String rid) throws POIException {
return read(FileUtil.file(path), rid);
}
/**
* 开始读取Excel
*
* @param file Excel文件
* @param rid Excel中的sheet rid编号如果为-1处理所有编号的sheet
* @return this
* @throws POIException POI异常
*/
T read(File file, int rid) throws POIException;
default T read(File file, int rid) throws POIException{
return read(file, String.valueOf(rid));
}
/**
* 开始读取Excel读取结束后并不关闭流
*
*
* @param in Excel流
* @param rid Excel中的sheet rid编号如果为-1处理所有编号的sheet
* @return this
* @throws POIException POI异常
*/
T read(InputStream in, int rid) throws POIException;
default T read(InputStream in, int rid) throws POIException{
return read(in, String.valueOf(rid));
};
}

View File

@ -73,9 +73,9 @@ public class ExcelSaxUtil {
}
break;
case NUMBER:
try{
try {
result = getNumberValue(value, numFmtString);
}catch (NumberFormatException e){
} catch (NumberFormatException e) {
result = value;
}
break;
@ -150,6 +150,7 @@ public class ExcelSaxUtil {
public static void readFrom(InputStream xmlDocStream, ContentHandler handler) throws DependencyException, POIException, IORuntimeException {
XMLReader xmlReader;
try {
// xmlReader = XMLReaderFactory.createXMLReader();
//noinspection deprecation
xmlReader = SAXHelper.newXMLReader();
} catch (SAXException | ParserConfigurationException e) {

View File

@ -0,0 +1,110 @@
package cn.hutool.poi.excel.sax;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.exceptions.POIException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* 在Sax方式读取Excel时读取sheet标签中sheetId和rid的对应关系类似于:
* <pre>
* &lt;sheet name="Sheet6" sheetId="4" r:id="6"/&gt;
* </pre>
*
* 读取结果为
*
* <pre>
* {"4": "6"}
* </pre>
*
* @author looly
* @since 5.4.4
*/
public class SheetRidReader extends DefaultHandler {
private final static String TAG_NAME = "sheet";
private final static String RID_ATTR = "r:id";
private final static String SHEET_ID_ATTR = "sheetId";
private final static String NAME_ATTR = "name";
private final Map<String, String> ID_RID_MAP = new HashMap<>();
private final Map<String, String> NAME_RID_MAP = new HashMap<>();
/**
* 读取Wordkbook的XML中sheet标签中sheetId和rid的对应关系
*
* @param xssfReader XSSF读取器
*/
public SheetRidReader read(XSSFReader xssfReader){
InputStream workbookData = null;
try{
workbookData = xssfReader.getWorkbookData();
ExcelSaxUtil.readFrom(workbookData, this);
} catch (InvalidFormatException e) {
throw new POIException(e);
} catch (IOException e) {
throw new IORuntimeException(e);
} finally {
IoUtil.close(workbookData);
}
return this;
}
/**
* 根据sheetId获取rid
*
* @param sheetId Sheet的ID
* @return rid
*/
public String getRidBySheetId(String sheetId){
return ID_RID_MAP.get(sheetId);
}
/**
* 根据sheet name获取rid
*
* @param sheetName Sheet的name
* @return rid
*/
public String getRidByName(String sheetName){
return NAME_RID_MAP.get(sheetName);
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if(TAG_NAME.equalsIgnoreCase(localName)){
final int length = attributes.getLength();
String sheetId = null;
String rid = null;
String name = null;
for (int i = 0; i < length; i++) {
switch (attributes.getLocalName(i)){
case SHEET_ID_ATTR:
sheetId = attributes.getValue(i);
break;
case RID_ATTR:
rid = attributes.getValue(i);
break;
case NAME_ATTR:
name = attributes.getValue(i);
break;
}
if(StrUtil.isNotEmpty(sheetId)){
ID_RID_MAP.put(sheetId, rid);
}
if(StrUtil.isNotEmpty(name)){
NAME_RID_MAP.put(name, rid);
}
}
}
}
}