diff --git a/pom.xml b/pom.xml index c818dd3..e85d044 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.alibaba easyexcel - 2.1.2 + 2.1.3 jar easyexcel diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java index f3ef950..eb4f188 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -176,7 +176,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { clearEncrypt03(); if (throwable != null) { - throw new ExcelAnalysisException("Can not close IO", throwable); + throw new ExcelAnalysisException("Can not close IO.", throwable); } } diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java index a30b426..2862be2 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java @@ -34,6 +34,7 @@ import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; import com.alibaba.excel.util.SheetUtils; +import com.alibaba.excel.util.StringUtils; /** * @@ -148,7 +149,13 @@ public class XlsxSaxAnalyser implements ExcelReadExecutor { private void parseXmlSource(InputStream inputStream, ContentHandler handler) { InputSource inputSource = new InputSource(inputStream); try { - SAXParserFactory saxFactory = SAXParserFactory.newInstance(); + SAXParserFactory saxFactory; + String xlsxSAXParserFactoryName = analysisContext.readWorkbookHolder().getXlsxSAXParserFactoryName(); + if (StringUtils.isEmpty(xlsxSAXParserFactoryName)) { + saxFactory = SAXParserFactory.newInstance(); + } else { + saxFactory = SAXParserFactory.newInstance(xlsxSAXParserFactoryName, null); + } saxFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); saxFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); saxFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index 3f2b51a..0935868 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -169,7 +169,9 @@ public class WriteContextImpl implements WriteContext { int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); newRowIndex += currentWriteHolder.relativeHeadRowIndex(); // Combined head - addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex); + if (currentWriteHolder.automaticMergeHead()) { + addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex); + } for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + newRowIndex; i++, relativeRowIndex++) { WriteHandlerUtils.beforeRowCreate(this, newRowIndex, relativeRowIndex, Boolean.TRUE); @@ -182,8 +184,9 @@ public class WriteContextImpl implements WriteContext { private void addMergedRegionToCurrentSheet(ExcelWriteHeadProperty excelWriteHeadProperty, int rowIndex) { for (com.alibaba.excel.metadata.CellRange cellRangeModel : excelWriteHeadProperty.headCellRangeList()) { - writeSheetHolder.getSheet().addMergedRegion(new CellRangeAddress(cellRangeModel.getFirstRow() + rowIndex, - cellRangeModel.getLastRow() + rowIndex, cellRangeModel.getFirstCol(), cellRangeModel.getLastCol())); + writeSheetHolder.getSheet() + .addMergedRegionUnsafe(new CellRangeAddress(cellRangeModel.getFirstRow() + rowIndex, + cellRangeModel.getLastRow() + rowIndex, cellRangeModel.getFirstCol(), cellRangeModel.getLastCol())); } } @@ -325,7 +328,7 @@ public class WriteContextImpl implements WriteContext { clearEncrypt03(); if (throwable != null) { - throw new ExcelGenerateException("Can not close IO", throwable); + throw new ExcelGenerateException("Can not close IO.", throwable); } if (LOGGER.isDebugEnabled()) { diff --git a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java index 340548b..4f6cdda 100644 --- a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java +++ b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java @@ -5,6 +5,8 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import javax.xml.parsers.SAXParserFactory; + import com.alibaba.excel.ExcelReader; import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.selector.ReadCacheSelector; @@ -233,6 +235,23 @@ public class ExcelReaderBuilder { return this; } + /** + * SAXParserFactory used when reading xlsx. + *

+ * The default will automatically find. + *

+ * Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl" + * + * @see SAXParserFactory#newInstance() + * @see SAXParserFactory#newInstance(String, ClassLoader) + * @param xlsxSAXParserFactoryName + * @return + */ + public ExcelReaderBuilder xlsxSAXParserFactoryName(String xlsxSAXParserFactoryName) { + readWorkbook.setXlsxSAXParserFactoryName(xlsxSAXParserFactoryName); + return this; + } + public ExcelReader build() { return new ExcelReader(readWorkbook); } diff --git a/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java b/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java index 3a873da..c6bbf7a 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java +++ b/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java @@ -3,6 +3,8 @@ package com.alibaba.excel.read.metadata; import java.io.File; import java.io.InputStream; +import javax.xml.parsers.SAXParserFactory; + import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.selector.ReadCacheSelector; import com.alibaba.excel.context.AnalysisContext; @@ -63,6 +65,17 @@ public class ReadWorkbook extends ReadBasicParameter { * Whether the encryption */ private String password; + /** + * SAXParserFactory used when reading xlsx. + *

+ * The default will automatically find. + *

+ * Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl" + * + * @see SAXParserFactory#newInstance() + * @see SAXParserFactory#newInstance(String, ClassLoader) + */ + private String xlsxSAXParserFactoryName; /** * The default is all excel objects.Default is true. *

@@ -176,4 +189,12 @@ public class ReadWorkbook extends ReadBasicParameter { public void setPassword(String password) { this.password = password; } + + public String getXlsxSAXParserFactoryName() { + return xlsxSAXParserFactoryName; + } + + public void setXlsxSAXParserFactoryName(String xlsxSAXParserFactoryName) { + this.xlsxSAXParserFactoryName = xlsxSAXParserFactoryName; + } } diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java index 35b3859..cbda93f 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java @@ -6,6 +6,8 @@ import java.io.InputStream; import java.util.HashSet; import java.util.Set; +import javax.xml.parsers.SAXParserFactory; + import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.poifs.filesystem.POIFSFileSystem; @@ -83,6 +85,17 @@ public class ReadWorkbookHolder extends AbstractReadHolder { * Whether the encryption */ private String password; + /** + * SAXParserFactory used when reading xlsx. + *

+ * The default will automatically find. + *

+ * Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl" + * + * @see SAXParserFactory#newInstance() + * @see SAXParserFactory#newInstance(String, ClassLoader) + */ + private String xlsxSAXParserFactoryName; /** * The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a * field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed. @@ -172,6 +185,7 @@ public class ReadWorkbookHolder extends AbstractReadHolder { } else { this.defaultReturnMap = readWorkbook.getDefaultReturnMap(); } + this.xlsxSAXParserFactoryName = readWorkbook.getXlsxSAXParserFactoryName(); this.hasReadSheet = new HashSet(); this.ignoreRecord03 = Boolean.FALSE; this.password = readWorkbook.getPassword(); @@ -321,6 +335,14 @@ public class ReadWorkbookHolder extends AbstractReadHolder { this.password = password; } + public String getXlsxSAXParserFactoryName() { + return xlsxSAXParserFactoryName; + } + + public void setXlsxSAXParserFactoryName(String xlsxSAXParserFactoryName) { + this.xlsxSAXParserFactoryName = xlsxSAXParserFactoryName; + } + @Override public HolderEnum holderType() { return HolderEnum.WORKBOOK; diff --git a/src/main/java/com/alibaba/excel/util/FileUtils.java b/src/main/java/com/alibaba/excel/util/FileUtils.java index 34e6853..23762f6 100644 --- a/src/main/java/com/alibaba/excel/util/FileUtils.java +++ b/src/main/java/com/alibaba/excel/util/FileUtils.java @@ -9,24 +9,50 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.UUID; +import org.apache.poi.util.DefaultTempFileCreationStrategy; +import org.apache.poi.util.TempFile; + import com.alibaba.excel.exception.ExcelAnalysisException; -import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.exception.ExcelCommonException; /** * * @author jipengfei */ public class FileUtils { + public static final String POI_FILES = "poifiles"; + public static final String EX_CACHE = "excache"; + /** + * If a server has multiple projects in use at the same time, a directory with the same name will be created under + * the temporary directory, but each project is run by a different user, so there is a permission problem, so each + * project creates a unique UUID as a separate Temporary Files. + */ + private static String tempFilePrefix = + System.getProperty(TempFile.JAVA_IO_TMPDIR) + UUID.randomUUID().toString() + File.separator; + /** + * Used to store poi temporary files. + */ + private static String poiFilesPath = tempFilePrefix + POI_FILES + File.separator; + /** + * Used to store easy excel temporary files. + */ + private static String cachePath = tempFilePrefix + EX_CACHE + File.separator; - private static final String JAVA_IO_TMPDIR = "java.io.tmpdir"; - - private static final String POIFILES = "poifiles"; - - private static final String CACHE = "excache"; private static final int WRITE_BUFF_SIZE = 8192; private FileUtils() {} + static { + // Create a temporary directory in advance + File tempFile = new File(tempFilePrefix); + createDirectory(tempFile); + tempFile.deleteOnExit(); + // Initialize the cache directory + File cacheFile = new File(cachePath); + createDirectory(cacheFile); + cacheFile.deleteOnExit(); + } + /** * Reads the contents of a file into a byte array. * The file is always closed. * @@ -106,19 +132,30 @@ public class FileUtils { } } - /** - */ public static void createPoiFilesDirectory() { - createTmpDirectory(POIFILES); + File poiFilesPathFile = new File(poiFilesPath); + createDirectory(poiFilesPathFile); + TempFile.setTempFileCreationStrategy(new DefaultTempFileCreationStrategy(poiFilesPathFile)); + poiFilesPathFile.deleteOnExit(); } public static File createCacheTmpFile() { - File directory = createTmpDirectory(CACHE); - File cache = new File(directory.getPath(), UUID.randomUUID().toString()); - if (!cache.mkdir()) { - throw new ExcelGenerateException("Can not create temp file!"); + return createDirectory(new File(cachePath + UUID.randomUUID().toString())); + } + + public static File createTmpFile(String fileName) { + return createDirectory(new File(tempFilePrefix + fileName)); + } + + /** + * + * @param directory + */ + private static File createDirectory(File directory) { + if (!directory.exists() && !directory.mkdirs()) { + throw new ExcelCommonException("Cannot create directory:" + directory.getAbsolutePath()); } - return cache; + return directory; } /** @@ -144,35 +181,27 @@ public class FileUtils { } } - public static File createTmpDirectory(String path) { - String tmpDir = System.getProperty(JAVA_IO_TMPDIR); - if (tmpDir == null) { - throw new RuntimeException( - "Systems temporary directory not defined - set the -D" + JAVA_IO_TMPDIR + " jvm property!"); - } - File directory = new File(tmpDir, path); - if (!directory.exists()) { - syncCreatePoiFilesDirectory(directory); - } - return directory; + public static String getTempFilePrefix() { + return tempFilePrefix; } - public static File createTmpFile(String fileName) { - String tmpDir = System.getProperty(JAVA_IO_TMPDIR); - if (tmpDir == null) { - throw new RuntimeException( - "Systems temporary directory not defined - set the -D" + JAVA_IO_TMPDIR + " jvm property!"); - } - return new File(tmpDir, fileName); + public static void setTempFilePrefix(String tempFilePrefix) { + FileUtils.tempFilePrefix = tempFilePrefix; } - /** - * - * @param directory - */ - private static synchronized void syncCreatePoiFilesDirectory(File directory) { - if (!directory.exists()) { - directory.mkdirs(); - } + public static String getPoiFilesPath() { + return poiFilesPath; + } + + public static void setPoiFilesPath(String poiFilesPath) { + FileUtils.poiFilesPath = poiFilesPath; + } + + public static String getCachePath() { + return cachePath; + } + + public static void setCachePath(String cachePath) { + FileUtils.cachePath = cachePath; } } diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java index f0d37ed..c1f2d94 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java @@ -91,6 +91,17 @@ public class ExcelWriterBuilder { return this; } + /** + * Whether to automatically merge headers.Default is true. + * + * @param automaticMergeHead + * @return + */ + public ExcelWriterBuilder automaticMergeHead(Boolean automaticMergeHead) { + writeWorkbook.setAutomaticMergeHead(automaticMergeHead); + return this; + } + /** * Whether the encryption. *

diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java index 50f230a..5cb71cc 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -86,6 +86,17 @@ public class ExcelWriterSheetBuilder { return this; } + /** + * Whether to automatically merge headers.Default is true. + * + * @param automaticMergeHead + * @return + */ + public ExcelWriterSheetBuilder automaticMergeHead(Boolean automaticMergeHead) { + writeSheet.setAutomaticMergeHead(automaticMergeHead); + return this; + } + /** * Custom type conversions override the default. * diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java index 40ca58b..2b461a9 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java @@ -90,6 +90,17 @@ public class ExcelWriterTableBuilder { return this; } + /** + * Whether to automatically merge headers.Default is true. + * + * @param automaticMergeHead + * @return + */ + public ExcelWriterTableBuilder automaticMergeHead(Boolean automaticMergeHead) { + writeTable.setAutomaticMergeHead(automaticMergeHead); + return this; + } + /** * Custom type conversions override the default. * @@ -129,7 +140,6 @@ public class ExcelWriterTableBuilder { return this; } - /** * Ignore the custom columns. */ diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java index 473d4e3..66cd4dd 100644 --- a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java @@ -21,7 +21,6 @@ import com.alibaba.excel.enums.WriteDirectionEnum; import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.metadata.CellData; -import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.StringUtils; @@ -69,6 +68,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { private Map> collectionLastIndexCache = new HashMap>(8); + private Map relativeRowIndexMap = new HashMap(8); + public ExcelWriteFillExecutor(WriteContext writeContext) { super(writeContext); } @@ -89,10 +90,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { shiftRows(collectionData.size(), analysisCellList); } while (iterator.hasNext()) { - doFill(analysisCellList, iterator.next(), fillConfig); + doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex()); } } else { - doFill(readTemplateData(templateAnalysisCache), data, fillConfig); + doFill(readTemplateData(templateAnalysisCache), data, fillConfig, null); } } @@ -138,7 +139,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } } - private void doFill(List analysisCellList, Object oneRowData, FillConfig fillConfig) { + private void doFill(List analysisCellList, Object oneRowData, FillConfig fillConfig, + Integer relativeRowIndex) { Map dataMap; if (oneRowData instanceof Map) { dataMap = (Map)oneRowData; @@ -161,7 +163,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { Object value = dataMap.get(variable); CellData cellData = converterAndSet(writeSheetHolder, value == null ? null : value.getClass(), cell, value, fieldNameContentPropertyMap.get(variable)); - WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, null, Boolean.FALSE); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE); } else { StringBuilder cellValueBuild = new StringBuilder(); int index = 0; @@ -197,11 +199,24 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } cellValueBuild.append(analysisCell.getPrepareDataList().get(index)); cell.setCellValue(cellValueBuild.toString()); - WriteHandlerUtils.afterCellDispose(writeContext, cellDataList, cell, null, null, Boolean.FALSE); + WriteHandlerUtils.afterCellDispose(writeContext, cellDataList, cell, null, relativeRowIndex, + Boolean.FALSE); } } } + private int getRelativeRowIndex() { + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); + Integer relativeRowIndex = relativeRowIndexMap.get(sheetNo); + if (relativeRowIndex == null) { + relativeRowIndex = 0; + } else { + relativeRowIndex++; + } + relativeRowIndexMap.put(sheetNo, relativeRowIndex); + return relativeRowIndex; + } + private Cell getOneCell(AnalysisCell analysisCell, FillConfig fillConfig) { Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { diff --git a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java b/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java index 13bdac3..b661c12 100644 --- a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java @@ -45,5 +45,5 @@ public abstract class AbstractMergeStrategy implements CellWriteHandler { * @param head * @param relativeRowIndex */ - protected abstract void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex); + protected abstract void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex); } diff --git a/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java b/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java index b087804..615e408 100644 --- a/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java @@ -39,14 +39,20 @@ public class LoopMergeStrategy extends AbstractMergeStrategy { } @Override - protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) { - if (head.getColumnIndex() == columnIndex && relativeRowIndex % eachRow == 0) { - CellRangeAddress cellRangeAddress = new CellRangeAddress( - cell.getRowIndex(), - cell.getRowIndex() + eachRow - 1, - cell.getColumnIndex(), - cell.getColumnIndex() + columnCount - 1); - sheet.addMergedRegion(cellRangeAddress); + protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { + if (relativeRowIndex == null) { + return; + } + Integer currentColumnIndex; + if (head != null) { + currentColumnIndex = head.getColumnIndex(); + } else { + currentColumnIndex = cell.getColumnIndex(); + } + if (currentColumnIndex == columnIndex && relativeRowIndex % eachRow == 0) { + CellRangeAddress cellRangeAddress = new CellRangeAddress(cell.getRowIndex(), + cell.getRowIndex() + eachRow - 1, cell.getColumnIndex(), cell.getColumnIndex() + columnCount - 1); + sheet.addMergedRegionUnsafe(cellRangeAddress); } } } diff --git a/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java b/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java index 6717b76..be05e22 100644 --- a/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java @@ -29,11 +29,11 @@ public class OnceAbsoluteMergeStrategy extends AbstractMergeStrategy { } @Override - protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) { + protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { if (cell.getRowIndex() == firstRowIndex && cell.getColumnIndex() == firstColumnIndex) { CellRangeAddress cellRangeAddress = new CellRangeAddress(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex); - sheet.addMergedRegion(cellRangeAddress); + sheet.addMergedRegionUnsafe(cellRangeAddress); } } } diff --git a/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java b/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java index 61115af..b582576 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java +++ b/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java @@ -29,6 +29,10 @@ public class WriteBasicParameter extends BasicParameter { * Use the default style.Default is true. */ private Boolean useDefaultStyle; + /** + * Whether to automatically merge headers.Default is true. + */ + private Boolean automaticMergeHead; /** * Ignore the custom columns. */ @@ -78,6 +82,14 @@ public class WriteBasicParameter extends BasicParameter { this.useDefaultStyle = useDefaultStyle; } + public Boolean getAutomaticMergeHead() { + return automaticMergeHead; + } + + public void setAutomaticMergeHead(Boolean automaticMergeHead) { + this.automaticMergeHead = automaticMergeHead; + } + public Collection getExcludeColumnIndexes() { return excludeColumnIndexes; } @@ -109,4 +121,5 @@ public class WriteBasicParameter extends BasicParameter { public void setIncludeColumnFiledNames(Collection includeColumnFiledNames) { this.includeColumnFiledNames = includeColumnFiledNames; } + } diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java index b28f267..375602a 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java @@ -66,6 +66,10 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ * Use the default style.Default is true. */ private Boolean useDefaultStyle; + /** + * Whether to automatically merge headers.Default is true. + */ + private Boolean automaticMergeHead; /** * Ignore the custom columns. */ @@ -127,6 +131,16 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ this.useDefaultStyle = writeBasicParameter.getUseDefaultStyle(); } + if (writeBasicParameter.getAutomaticMergeHead() == null) { + if (parentAbstractWriteHolder == null) { + this.automaticMergeHead = Boolean.TRUE; + } else { + this.automaticMergeHead = parentAbstractWriteHolder.getAutomaticMergeHead(); + } + } else { + this.automaticMergeHead = writeBasicParameter.getAutomaticMergeHead(); + } + if (writeBasicParameter.getExcludeColumnFiledNames() == null && parentAbstractWriteHolder != null) { this.excludeColumnFiledNames = parentAbstractWriteHolder.getExcludeColumnFiledNames(); } else { @@ -251,7 +265,7 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ } writeBasicParameter.getCustomWriteHandlerList().add(new AbstractHeadColumnWidthStyleStrategy() { @Override - protected Integer columnWidth(Head head) { + protected Integer columnWidth(Head head, Integer columnIndex) { if (columnWidthMap.containsKey(head.getColumnIndex())) { return columnWidthMap.get(head.getColumnIndex()) / 256; } @@ -300,7 +314,7 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ private void dealColumnWidth(List handlerList) { WriteHandler columnWidthStyleStrategy = new AbstractHeadColumnWidthStyleStrategy() { @Override - protected Integer columnWidth(Head head) { + protected Integer columnWidth(Head head, Integer columnIndex) { if (head == null) { return null; } @@ -441,6 +455,14 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ this.useDefaultStyle = useDefaultStyle; } + public Boolean getAutomaticMergeHead() { + return automaticMergeHead; + } + + public void setAutomaticMergeHead(Boolean automaticMergeHead) { + this.automaticMergeHead = automaticMergeHead; + } + public Collection getExcludeColumnIndexes() { return excludeColumnIndexes; } @@ -492,4 +514,9 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ public int relativeHeadRowIndex() { return getRelativeHeadRowIndex(); } + + @Override + public boolean automaticMergeHead() { + return getAutomaticMergeHead(); + } } diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java index 8f2ae7e..2aa2066 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java @@ -44,6 +44,13 @@ public interface WriteHolder extends ConfigurationHolder { */ boolean needHead(); + /** + * Whether need automatic merge headers. + * + * @return + */ + boolean automaticMergeHead(); + /** * Writes the head relative to the existing contents of the sheet. Indexes are zero-based. * diff --git a/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java index 9908144..1a88eff 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java @@ -22,7 +22,7 @@ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColum if (!needSetWidth) { return; } - Integer width = columnWidth(head); + Integer width = columnWidth(head, cell.getColumnIndex()); if (width != null) { width = width * 256; writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), width); @@ -36,9 +36,11 @@ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColum * if return null,ignore * * @param head - * Nullable + * Nullable. + * @param columnIndex + * Not null. * @return */ - protected abstract Integer columnWidth(Head head); + protected abstract Integer columnWidth(Head head, Integer columnIndex); } diff --git a/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java index 1f3ae78..316ff2c 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java @@ -19,7 +19,7 @@ public class SimpleColumnWidthStyleStrategy extends AbstractHeadColumnWidthStyle } @Override - protected Integer columnWidth(Head head) { + protected Integer columnWidth(Head head, Integer columnIndex) { return columnWidth; } } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java index dcdf23f..a1dd56a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java @@ -14,6 +14,6 @@ public class FillData { private String name; @NumberFormat("#") @ExcelProperty(converter = DoubleStringConverter.class) - private double number; + private Double number; private String empty; } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java index 93a5457..04a8bd1 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java @@ -6,35 +6,19 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.poi.ss.usermodel.BorderStyle; -import org.apache.poi.ss.usermodel.FillPatternType; -import org.apache.poi.ss.usermodel.Font; -import org.apache.poi.ss.usermodel.HorizontalAlignment; -import org.apache.poi.ss.usermodel.IndexedColors; -import org.apache.poi.ss.usermodel.VerticalAlignment; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; -import com.alibaba.easyexcel.test.core.style.StyleData; -import com.alibaba.easyexcel.test.core.style.StyleDataListener; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.enums.WriteDirectionEnum; -import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.merge.LoopMergeStrategy; -import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.fill.FillConfig; -import com.alibaba.excel.write.metadata.style.WriteCellStyle; -import com.alibaba.excel.write.metadata.style.WriteFont; -import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy; -import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; -import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy; -import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy; /** * @@ -121,7 +105,7 @@ public class FillDataTest { private void complexFill(File file, File template) { ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); + WriteSheet writeSheet = EasyExcel.writerSheet().registerWriteHandler(new LoopMergeStrategy(2, 0)).build(); FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); excelWriter.fill(data(), fillConfig, writeSheet); excelWriter.fill(data(), fillConfig, writeSheet); diff --git a/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexHeadDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexHeadDataTest.java index f893684..25f7fed 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexHeadDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexHeadDataTest.java @@ -18,11 +18,15 @@ public class ComplexHeadDataTest { private static File file07; private static File file03; + private static File file07AutomaticMergeHead; + private static File file03AutomaticMergeHead; @BeforeClass public static void init() { file07 = TestFileUtil.createNewFile("complexHead07.xlsx"); file03 = TestFileUtil.createNewFile("complexHead03.xls"); + file07AutomaticMergeHead = TestFileUtil.createNewFile("complexHeadAutomaticMergeHead07.xlsx"); + file03AutomaticMergeHead = TestFileUtil.createNewFile("complexHeadAutomaticMergeHead03.xls"); } @Test @@ -37,6 +41,22 @@ public class ComplexHeadDataTest { private void readAndWrite(File file) { EasyExcel.write(file, ComplexHeadData.class).sheet().doWrite(data()); + EasyExcel.read(file, ComplexHeadData.class, new ComplexDataListener()) + .xlsxSAXParserFactoryName("com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl").sheet().doRead(); + } + + @Test + public void t03ReadAndWriteAutomaticMergeHead07() { + readAndWriteAutomaticMergeHead07(file07AutomaticMergeHead); + } + + @Test + public void t04ReadAndWriteAutomaticMergeHead0703() { + readAndWriteAutomaticMergeHead07(file03AutomaticMergeHead); + } + + private void readAndWriteAutomaticMergeHead07(File file) { + EasyExcel.write(file, ComplexHeadData.class).automaticMergeHead(Boolean.FALSE).sheet().doWrite(data()); EasyExcel.read(file, ComplexHeadData.class, new ComplexDataListener()).sheet().doRead(); } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java index fb39de1..27f820b 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java @@ -39,7 +39,7 @@ public class LockTest { @Test public void test2() throws Exception { List list = - EasyExcel.read(new FileInputStream("D:\\test\\null.xlsx")).sheet().headRowNumber(0).doReadSync(); + EasyExcel.read(new FileInputStream("D:\\test\\开发部.xls")).sheet().headRowNumber(0).doReadSync(); for (Object data : list) { LOGGER.info("返回数据:{}", ((Map)data).size()); LOGGER.info("返回数据:{}", JSON.toJSONString(data)); diff --git a/update.md b/update.md index 4b6e5a4..757de15 100644 --- a/update.md +++ b/update.md @@ -1,3 +1,12 @@ +# 2.1.3 +* 每个java进程单独创建一个缓存目录 [Issue #813](https://github.com/alibaba/easyexcel/issues/813) +* 统一修改合并为unsafe,提高大量数据导出的合并的效率 +* 修改merge返回参数`relativeRowIndex`为`Integer` +* 新增参数`automaticMergeHead` 可以设置不自动合并头 [Issue #822](https://github.com/alibaba/easyexcel/issues/822) +* 新增参数`xlsxSAXParserFactoryName` 可以指定`SAXParserFactory` +* 修复合并策略 空指针的问题 +* `SimpleColumnWidthStyleStrategy` 新增 参数`columnIndex` [Issue #806](https://github.com/alibaba/easyexcel/issues/806) + # 2.1.2 * 修复强制创建新行填充,只有一行数据会未填充的bug