项目中使用了 easyexcel,依赖如下
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version>
</dependency>
项目中使用easyexcel导出文件,没有指定文件类型为xls,但是创建的文件后缀为xls,却提示超出65536行,想到这是excel 2003以下的限制,指定了类型后好了。
问题如下
查看源码
package com.alibaba.excel.support;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelCommonException;
import com.alibaba.excel.read.metadata.ReadWorkbook;
import com.alibaba.excel.util.StringUtils;
import lombok.Getter;
import org.apache.poi.util.IOUtils;
/**
* @author jipengfei
*/
@Getter
public enum ExcelTypeEnum {
/**
* csv
*/
CSV(".csv", new byte[] {-27, -89, -109, -27}),
/**
* xls
*/
XLS(".xls", new byte[] {-48, -49, 17, -32, -95, -79, 26, -31}),
/**
* xlsx
*/
XLSX(".xlsx", new byte[] {80, 75, 3, 4});
final String value;
final byte[] magic;
ExcelTypeEnum(String value, byte[] magic) {
this.value = value;
this.magic = magic;
}
final static int MAX_PATTERN_LENGTH = 8;
public static ExcelTypeEnum valueOf(ReadWorkbook readWorkbook) {
ExcelTypeEnum excelType = readWorkbook.getExcelType();
if (excelType != null) {
return excelType;
}
File file = readWorkbook.getFile();
InputStream inputStream = readWorkbook.getInputStream();
if (file == null && inputStream == null) {
throw new ExcelAnalysisException("File and inputStream must be a non-null.");
}
try {
if (file != null) {
if (!file.exists()) {
throw new ExcelAnalysisException("File " + file.getAbsolutePath() + " not exists.");
}
// If there is a password, use the FileMagic first
if (!StringUtils.isEmpty(readWorkbook.getPassword())) {
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) {
return recognitionExcelType(bufferedInputStream);
}
}
// Use the name to determine the type
String fileName = file.getName();
if (fileName.endsWith(XLSX.getValue())) {
return XLSX;
} else if (fileName.endsWith(XLS.getValue())) {
return XLS;
} else if (fileName.endsWith(CSV.getValue())) {
return CSV;
}
if (StringUtils.isEmpty(readWorkbook.getPassword())) {
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) {
return recognitionExcelType(bufferedInputStream);
}
}
}
if (!inputStream.markSupported()) {
inputStream = new BufferedInputStream(inputStream);
readWorkbook.setInputStream(inputStream);
}
return recognitionExcelType(inputStream);
} catch (ExcelCommonException e) {
throw e;
} catch (Exception e) {
throw new ExcelCommonException(
"Convert excel format exception.You can try specifying the 'excelType' yourself", e);
}
}
private static ExcelTypeEnum recognitionExcelType(InputStream inputStream) throws Exception {
// Grab the first bytes of this stream
byte[] data = IOUtils.peekFirstNBytes(inputStream, MAX_PATTERN_LENGTH);
if (findMagic(XLSX.magic, data)) {
return XLSX;
} else if (findMagic(CSV.magic, data)) {
return CSV;
} else if (findMagic(XLS.magic, data)) {
return XLS;
}
throw new ExcelCommonException(
"Convert excel format exception.You can try specifying the 'excelType' yourself");
}
private static boolean findMagic(byte[] expected, byte[] actual) {
int i = 0;
for (byte expectedByte : expected) {
if (actual[i++] != expectedByte && expectedByte != '?') {
return false;
}
}
return true;
}
}
通过静态方法 valueOf() 得知,最终文件类型是通过创建的 excel 文件后缀来最终指定文件类型是xls还是xlsx。
其中 valueOf() 在 ExcelAnalyserImpl 的 choiceExcelExecutor() 被引用
private void choiceExcelExecutor(ReadWorkbook readWorkbook) throws Exception {
ExcelTypeEnum excelType = ExcelTypeEnum.valueOf(readWorkbook);
switch (excelType) {
case XLS:
POIFSFileSystem poifsFileSystem;
if (readWorkbook.getFile() != null) {
poifsFileSystem = new POIFSFileSystem(readWorkbook.getFile());
} else {
poifsFileSystem = new POIFSFileSystem(readWorkbook.getInputStream());
}
// So in encrypted excel, it looks like XLS but it's actually XLSX
if (poifsFileSystem.getRoot().hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)) {
InputStream decryptedStream = null;
try {
decryptedStream = DocumentFactoryHelper
.getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), readWorkbook.getPassword());
XlsxReadContext xlsxReadContext = new DefaultXlsxReadContext(readWorkbook, ExcelTypeEnum.XLSX);
analysisContext = xlsxReadContext;
excelReadExecutor = new XlsxSaxAnalyser(xlsxReadContext, decryptedStream);
return;
} finally {
IOUtils.closeQuietly(decryptedStream);
// as we processed the full stream already, we can close the filesystem here
// otherwise file handles are leaked
poifsFileSystem.close();
}
}
if (readWorkbook.getPassword() != null) {
Biff8EncryptionKey.setCurrentUserPassword(readWorkbook.getPassword());
}
XlsReadContext xlsReadContext = new DefaultXlsReadContext(readWorkbook, ExcelTypeEnum.XLS);
xlsReadContext.xlsReadWorkbookHolder().setPoifsFileSystem(poifsFileSystem);
analysisContext = xlsReadContext;
excelReadExecutor = new XlsSaxAnalyser(xlsReadContext);
break;
case XLSX:
XlsxReadContext xlsxReadContext = new DefaultXlsxReadContext(readWorkbook, ExcelTypeEnum.XLSX);
analysisContext = xlsxReadContext;
excelReadExecutor = new XlsxSaxAnalyser(xlsxReadContext, null);
break;
case CSV:
CsvReadContext csvReadContext = new DefaultCsvReadContext(readWorkbook, ExcelTypeEnum.CSV);
analysisContext = csvReadContext;
excelReadExecutor = new CsvExcelReadExecutor(csvReadContext);
break;
default:
break;
}
}
综上所述:得知最终通过文件后缀判断枚举类型来创建对应的文件格式,所以,无需指定 excelType。
ExcelWriter excelWriter = EasyExcel.write(filePath).excelType(ExcelTypeEnum.XLSX).registerWriteHandler(new CustomCellWeightWeightConfig()).build();
设置 excelType 如下
通过 EasyExcel 间接生成 ExcelWriterBuilder 类,类中有全局变量 writeWorkbook,全局变量 writeWorkbook 中变量 excelType
public class WriteWorkbook extends WriteBasicParameter {
/**
* Excel type.The default is xlsx
*/
private ExcelTypeEnum excelType;
这里有一个疑问:看到注释中有默认 excel 类型是 xlsx,莫非是后面改的按文件后缀来生成对应类型的文件,然而这里的注释忘了改?