前言
我们知道Excel在2003版本和2007版本有区别,03版本excel后缀未.xls,而07版本后缀未.xlsx,其次03版本最多只能有65536条记录,07版本的最多1048576条记录。
简单了解下Excel的简单组成:
整个excel文件叫做工作簿。有这些知识之后看下面的内容就很轻松了。
一、POI
API文档地址:https://tool.oschina.net/apidocs/apidoc?api=apache-POI
中文文档参照:https://www.cnblogs.com/fqfanqi/p/6172223.html
maven依赖信息:
<dependencies>
<!--03版本Excel(xls)-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<!--07版本Excel(xlsx)-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<!--日期格式化工具-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.1</version>
</dependency>
<!--单元测试工具-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
在使用中,主要是创建工作簿使用的实现类不一样。
2003版本的excel
- 大文件写HSSF
- 优点:过程中写入缓存,不操作磁盘,最后一次写入磁盘,速度快
- 缺点:最多只能处理65536行,否则抛出异常
2007版本excel
- 大文件写XSSF
- 优点:可以写入较大的数据量,20w…
- 缺点:写数据时速度非常慢,非常耗内存,也会发生内存溢出,100w条…
2007版本excel
- 大文件写SXSSF(XSSF的加速版本)
- 优点:可以写入较大的数据量,100w+,写数据速度快,占更少的内存
- 注意:过程中会产生临时文件,需要清理零时文件
- 默认由100条记录被保存在内存中,如果超过这个数量,则最前面的数据被写入临时文件
- 如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)
SXSSFWorkbook来自官方的解释:实现BigGridDemo
策略的流式XSSFWorkbook
版本,这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。请注意:仍然可能会消耗大量的内存,这些内存基于您正在使用的功能,例如合并区域,注释…仍然只存储在内存中,因此,如果广泛使用,可能需要大量内存。
需要注意的是Excel中下标都是从0开始的。
写文件代码(一个方法对应一次测试):
package com.dl;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class ExcelWriteTest {
private String Path="E:\\5120154230JavaCode\\POIAndEasyExcel\\POI\\"; //文件保存路径
/**
* 03版本的excel
* 大文件写HSSF
* 优点:过程中写入缓存,不操作磁盘,最后一次写入磁盘,速度快
* 缺点:最多只能处理65536行,否则抛出异常
* @throws IOException
*/
public void testWrite03() throws IOException {
//1、创建一个工作簿
Workbook workbook=new HSSFWorkbook();
//2、创建一个工作表(可以选择按照下标或者名字创建)
Sheet sheet=workbook.createSheet("统计表1");
//3、创建一个行
Row row1=sheet.createRow(0);
//4、创建一个单元格
Cell cell11 = row1.createCell(0);
cell11.setCellValue("王小二");
Cell cell12 = row1.createCell(1);
cell12.setCellValue("tonghua");
Row row2=sheet.createRow(1);
Cell cell21 = row2.createCell(1);
cell21.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss"));
FileOutputStream fileOutputStream = new FileOutputStream(Path + "tonghua统计表1.xls");
workbook.write(fileOutputStream);
fileOutputStream.close();
}
/**
* 07版本excel
* 大文件写XSSF
* 优点:可以写入较大的数据量,20w...
* 缺点:写数据时速度非常慢,非常耗内存,也会发生内存移除,100w条...
* @throws IOException
*/
public void testWrite07() throws IOException {
//1、创建一个工作簿
Workbook workbook=new XSSFWorkbook();
//2、创建一个工作表
Sheet sheet=workbook.createSheet("统计表1");
//3、创建一个行
Row row1=sheet.createRow(0);
//4、创建一个单元格
Cell cell11 = row1.createCell(0);
cell11.setCellValue("王小二");
Cell cell12 = row1.createCell(1);
cell12.setCellValue("tonghua");
Row row2=sheet.createRow(1);
Cell cell21 = row2.createCell(1);
cell21.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss"));
FileOutputStream fileOutputStream = new FileOutputStream(Path + "tonghua统计表1.xlsx");
workbook.write(fileOutputStream);
fileOutputStream.close();
}
public void testWrite03BigData() throws IOException {
long start=System.currentTimeMillis();
//1、创建一个工作簿
Workbook workbook=new HSSFWorkbook();
//2、创建一个工作表
Sheet sheet=workbook.createSheet("统计表1");
//3、创建一个行
for(int row=0;row<65536;row++){
Row row1 = sheet.createRow(row);
for (int cell=0;cell<10;cell++){
Cell cell1 = row1.createCell(cell);
cell1.setCellValue(cell);
}
}
FileOutputStream fileOutputStream = new FileOutputStream(Path + "BigData03.xls");
workbook.write(fileOutputStream);
long end = System.currentTimeMillis();
System.out.println("耗时:"+(double)(end-start)/1000+"秒!");
fileOutputStream.close();
}
/**
* 07版本耗时多
* @throws IOException
*/
public void testWrite07BigData() throws IOException {
long start=System.currentTimeMillis();
//1、创建一个工作簿
Workbook workbook=new XSSFWorkbook();
//2、创建一个工作表
Sheet sheet=workbook.createSheet("统计表1");
//3、创建一个行
for(int row=0;row<65536;row++){
Row row1 = sheet.createRow(row);
for (int cell=0;cell<10;cell++){
Cell cell1 = row1.createCell(cell);
cell1.setCellValue(cell);
}
}
FileOutputStream fileOutputStream = new FileOutputStream(Path + "BigData07.xlsx");
workbook.write(fileOutputStream);
long end = System.currentTimeMillis();
System.out.println("耗时:"+(double)(end-start)/1000+"秒!");
fileOutputStream.close();
}
public void testWrite07BigDataS() throws IOException {
long start=System.currentTimeMillis();
//1、创建一个工作簿
Workbook workbook=new SXSSFWorkbook();
//2、创建一个工作表
Sheet sheet=workbook.createSheet("统计表1");
//3、创建一个行
for(int row=0;row<65555;row++){
Row row1 = sheet.createRow(row);
for (int cell=0;cell<10;cell++){
Cell cell1 = row1.createCell(cell);
cell1.setCellValue(cell);
}
}
FileOutputStream fileOutputStream = new FileOutputStream(Path + "BigDataS07.xlsx");
workbook.write(fileOutputStream);
long end = System.currentTimeMillis();
System.out.println("耗时:"+(double)(end-start)/1000+"秒!");
fileOutputStream.close();
((SXSSFWorkbook)workbook).dispose();//清除临时文件
}
/**
* 07版本excel
* 大文件写SXSSF
* 优点:可以写入较大的数据量,100w+,写数据速度快,占更少的内存
* 注意:过程中会产生临时文件,需要清理零时文件
* 默认由100条记录被保存在内存中,如果超过这个数量,则最前面的数据被写入临时文件
* 如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)
*
* SXSSFWorkbook来自官方的解释:实现BigGridDemo策略的流式XSSFWorkbook版本,这允许写入非常大的文件而不会耗尽内存
* 因为任何时候只有可配置的行部分被保存在内存中。
* 请注意:仍然可能会消耗大量的内存,这些内存基于您正在使用的功能,例如合并区域,注释...仍然只存储在内存中,因此,如果
* 广泛使用,可能需要大量内存。
* @throws IOException
*/
public void testWrite0707() throws IOException {
//1、创建一个工作簿
Workbook workbook=new SXSSFWorkbook();
//2、创建一个工作表
Sheet sheet=workbook.createSheet("统计表1");
//3、创建一个行
Row row1=sheet.createRow(0);
//4、创建一个单元格
Cell cell11 = row1.createCell(0);
cell11.setCellValue("王小二");
Cell cell12 = row1.createCell(1);
cell12.setCellValue("dignli");
Row row2=sheet.createRow(1);
Cell cell21 = row2.createCell(1);
cell21.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss"));
FileOutputStream fileOutputStream = new FileOutputStream(Path + "tonghua统计表1.xlsx");
workbook.write(fileOutputStream);
fileOutputStream.close();
}
public static void main(String[] args) throws IOException {
ExcelWriteTest excelWriteTest=new ExcelWriteTest();
// excelWriteTest.testWrite03BigData();
excelWriteTest.testWrite07BigDataS();
// excelWriteTest.testWrite07();
}
}
读文件代码
package com.dl;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Date;
import static org.apache.poi.ss.usermodel.CellType.*;
public class ExcelReadTest {
private String Path="E:\\5120154230JavaCode\\POIAndEasyExcel\\POI\\tonghua统计表1.xls";
@Test
public void test03Read() throws IOException {
FileInputStream fileInputStream=new FileInputStream(Path);
Workbook workbook=new HSSFWorkbook(fileInputStream);
Sheet sheet=workbook.getSheetAt(0);
Row row = sheet.getRow(0);
//注意获取值的类型
System.out.println(row.getCell(0).getStringCellValue());
System.out.println(row.getCell(1).getNumericCellValue());
fileInputStream.close();
}
private String PATH="E:\\5120154230JavaCode\\POIAndEasyExcel\\POI\\tonghua统计表1.xlsx";
@Test
public void testCellType() throws IOException {
FileInputStream fileInputStream=new FileInputStream(PATH);
if(fileInputStream==null){
System.out.println("文件不存在!");
}
Workbook workbook= new XSSFWorkbook(fileInputStream);
Sheet sheetAt = workbook.getSheetAt(0);
if(sheetAt!=null){
//标题
Row rowTitle = sheetAt.getRow(0);
int cells = rowTitle.getPhysicalNumberOfCells();
for (int cell = 0; cell < cells; cell++) {
if(rowTitle!=null){
System.out.print(rowTitle.getCell(cell)+"||");
}
}
System.out.println();
//标题下的各个单元格内容
int rows=sheetAt.getPhysicalNumberOfRows();//得到行数
for(int row=1;row<rows;row++){
Row rowData=sheetAt.getRow(row);
if(rowData!=null){
int cellDataNums=rowData.getPhysicalNumberOfCells();
for(int cellDataNum=0;cellDataNum<cellDataNums;cellDataNum++){
System.out.print(row+"--"+cellDataNum);
Cell cellData = rowData.getCell(cellDataNum);
if(cellData!=null){
// System.out.println(cellData);
CellType cellTypeEnum = cellData.getCellTypeEnum();
String cellValue="";
switch (cellTypeEnum){
//判断单元格内容类型
case STRING:
System.out.print("[String] ");
cellValue=cellData.getStringCellValue();
break;
case BLANK:
System.out.print("[BLANK] ");
break;
case BOOLEAN:
System.out.print("[Boolean] ");
cellValue=String.valueOf(cellData.getBooleanCellValue());
break;
case NUMERIC: //数字
System.out.print("[Numeric] ");
if(HSSFDateUtil.isCellDateFormatted(cellData)){
Date date=cellData.getDateCellValue();
cellValue=new DateTime(date).toString("yyyy-MM-dd HH:mm:ss");
}else{
cellValue=String.valueOf(cellData.getNumericCellValue());
BigDecimal db = new BigDecimal(cellValue);
cellValue = db.toPlainString();
}
break;
case ERROR:
System.out.print("ERROR");
break;
}
System.out.println(cellValue);
}
}
}
}
}
fileInputStream.close(); //关闭文件输入流
}
}
二、EasyExcel
云雀地址:https://www.yuque.com/easyexcel/doc/easyexcel
Maven依赖:
<!--需要注意的是easyexcel中包含了03版本和07版本的poi依赖,额外导入poi依赖需要删除掉,否则可能出现jar包冲突-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--JSON序列化工具-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.74</version>
</dependency>
<!--POJO实体类插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--日期格式化工具-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.1</version>
</dependency>
lombok还需要在idea中安装lombok插件。
这个操作直接看云雀上的教程即可,非常简单。
POI操作Excel通用工具类
Maven依赖:
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
<!--里面已经包含了POI相关依赖,不排除则会依赖冲突-->
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<!-- <dependency>-->
<!-- <groupId>org.apache.poi</groupId>-->
<!-- <artifactId>poi</artifactId>-->
<!-- <version>4.0.1</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.apache.poi</groupId>-->
<!-- <artifactId>poi-ooxml</artifactId>-->
<!-- <version>4.0.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.74</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--日期格式化工具-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
</dependencies>
首先创建一个注解用于读取实体类属性。
1、自定义注解
package com.dl.Utils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CellField {
/**
* 文件列名
*/
public String name() default "";
}
2、实体类
package com.dl.POJO;
import com.dl.Utils.CellField;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 文件信息实体类
*/
public class IPOFileInfo {
/**文件所属类型*/
@CellField(name = "文件类型")
private String fileType;
/**文件所属编码 */
@CellField(name = "文件编码")
private String fileCode;
/**文件名称 */
@CellField(name = "文件名称")
private String fileName;
/**文件URL */
@CellField(name = "文件URL")
private String fileUrl;
/**文件日期 */
@CellField(name = "文件日期")
private String fileDate;
/**返回的文件id */
@CellField(name = "文件id")
private String fileId;
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public String getFileCode() {
return fileCode;
}
public void setFileCode(String fileCode) {
this.fileCode = fileCode;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileUrl() {
return fileUrl;
}
public void setFileUrl(String fileUrl) {
this.fileUrl = fileUrl;
}
public String getFileDate() {
return fileDate;
}
public void setFileDate(String fileDate) {
this.fileDate = fileDate;
}
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
3、工具类
package com.dl.Utils;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.FileMagic;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
/**
* Excel工具类
*/
public final class ExcelUtils {
/** 日志对象 **/
private static final Logger LOGGER = LoggerFactory.getLogger(ExcelUtils.class);
/** 私有无参构造方法 **/
private ExcelUtils() {
}
/**
* 生成excel2007文档
*
* @param filePath 文件保存路径 ,例如:D:/excel/test.xlsx
* @param beans 实体对象
*/
public static <T> void createExcel(String filePath, List<T> beans) throws FileNotFoundException {
String fileName = null;
if(filePath.contains("/")){
fileName=filePath.substring(filePath.lastIndexOf('/') + 1);
}else if(filePath.contains("\\")){
fileName=filePath.substring(filePath.lastIndexOf('\\') + 1);
}else{
fileName="sheet1";
}
Workbook wb = createWorkbook(beans, fileName);
File file = createFile(filePath);
try (OutputStream os = new FileOutputStream(file)) {
wb.write(os);
} catch (IOException e) {
LOGGER.error("生成excel2007文件失败", e);
}
}
/**
* 导出excel2007文件
*
* @param response 响应对象
* @param beans 实体对象集合
*/
public static <T> void exportExcel(HttpServletResponse response, List<T> beans) {
// 以当前时间作为文件名
String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
// 创建Workbook对象
Workbook wb = createWorkbook(beans, fileName);
// 导出excel
try (OutputStream os = response.getOutputStream()) {
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Expose-Headers", "*");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
wb.write(os);
} catch (IOException e) {
LOGGER.error("导出excel2007文件失败", e);
}
}
/**
* 导出excel2007文件
*
* @param response 响应对象
* @param beans 实体对象集合
* @param fileName 文件名称 ,如:测试名称
*/
public static <T> void exportExcel(HttpServletResponse response, List<T> beans, String fileName) {
// 创建Workbook对象
Workbook wb = createWorkbook(beans, fileName);
// 导出excel
try (OutputStream os = response.getOutputStream()) {
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Expose-Headers", "*");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
wb.write(os);
} catch (IOException e) {
LOGGER.error("导出excel2007文件失败", e);
}
}
/**
* 导出excel2007模板
*
* @param response 响应对象
* @param clazz<T> 类类型
*/
public static <T> void exportExcelTemplate(HttpServletResponse response, Class<T> clazz) {
// 以当前时间作为文件名
String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
exportExcelTemplate(response, clazz, fileName);
}
/**
* 导出excel2007模板
*
* @param response 响应对象
* @param clazz<T> 类类型
* @param fileName 文件名称
*/
public static <T> void exportExcelTemplate(HttpServletResponse response, Class<T> clazz, String fileName) {
// 1.创建2007版工作簿
Workbook wb = new XSSFWorkbook();
// 2.创建工作表
Sheet sheet = wb.createSheet(fileName);
// 3.获取实体类标有@CellField注解的Field对象
List<Field> fields = new ArrayList<Field>();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
CellField anno = field.getAnnotation(CellField.class);
if (anno != null) {
fields.add(field);
}
}
// 4.设置列宽
for (int i = 0; i < fields.size(); i++) {
sheet.setColumnWidth((short) i, (short) (20.7 * 150));
}
// 5.写入标题
Row row = sheet.createRow(0);
writeTitles(fields, row);
// 6.导出excel模板
try (OutputStream os = response.getOutputStream()) {
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Expose-Headers", "*");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
wb.write(os);
} catch (IOException e) {
LOGGER.error("导出excel2007模板失败", e);
} finally {
if (wb != null) {
try {
wb.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 解析Excel文档,转成对象集合返回
*
* @param filePath 文件存放路径,例如:D:/excel/test.xlsx
* @param clazz 泛型类类型
* @return List<T> 对象集合
*/
public static <T> List<T> parseExcel(String filePath, Class<T> clazz) {
List<T> list = new ArrayList<>();
InputStream input = null;
try {
input = new FileInputStream(new File(filePath));
if (input != null && !input.markSupported()) {
input = new PushbackInputStream(input, 8);
}
Workbook wb = getWorkbook(input,filePath);
// final XSSFWorkbook wb = new XSSFWorkbook(input);
if (wb != null) {
list = getBeanList(wb, clazz);
}
} catch (Exception e) {
LOGGER.error("解析文件失败", e);
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
LOGGER.error("关闭流失败", e);
}
}
}
return list;
}
/**
* 删除该目录下所有文件
* @param filePath 文件目录路径,如:d:/test/
* @return boolean
*/
public static boolean deleteFiles(String filePath) {
File file = new File(filePath);
if (file.exists()) {
File[] files = file.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
if (f.isFile() && f.delete()) {
LOGGER.info("删除" + f.getName() + "文件成功");
}
}
return true;
}
}
return false;
}
/**
* 删除指定文件
* @param filePath 文件目录路径,如:d:/test/
* @param fileName 文件名称,如:test.xlsx
* @return boolean
*/
public static boolean deleteFile(String filePath, String fileName) {
File file = new File(filePath);
if (file.exists()) {
File[] files = file.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
if (f.isFile() && f.getName().equals(fileName)) {
return f.delete();
}
}
}
}
return false;
}
/**
* 获取Workbook对象
*
* @param input 输入流
* @return Workbook
*/
private static Workbook getWorkbook(InputStream input,String filePath) {
try {
// if (POIFSFileSystem.hasPOIFSHeader(input)) {
// return new HSSFWorkbook(input); // 得到2003工作簿
// } else {
// try (OPCPackage op = OPCPackage.open(input)) {
// return new XSSFWorkbook(op); // 得到2007工作簿
// } catch (Exception e) {
// LOGGER.error("获取Workbook对象失败", e);
// }
// }
if(filePath.endsWith(".xls")){
//03版本
return new HSSFWorkbook(input);
}else{
//07版本
return new XSSFWorkbook(input);
}
} catch (IOException e) {
LOGGER.error("获取Workbook对象失败", e);
}
return null;
}
/**
* 通过泛型转换为对象集合
*
* @param wb 工作簿对象
* @param clazz 实体对象类类型
* @param <T> 泛型类型
* @return <T> 泛型实体对象
*/
private static <T> List<T> getBeanList(Workbook wb, Class<T> clazz) {
List<T> list = new ArrayList<T>();
// 注解名称与字段属性的对应关系
Map<String, Field> annoMap = getFields(clazz);
// 列所引与标题名称对应关系
Map<Integer, String> titleMap = new HashMap<Integer, String>();
Sheet sheet = wb.getSheetAt(0); // 获取第一张工作表
Iterator<Row> rows = sheet.iterator(); // 利用迭代器,取出每一个行
int index = 0;
while (rows.hasNext()) {
Row row = rows.next(); // 每一行
Iterator<Cell> cells = row.iterator(); // 利用迭代器,取出每一个格
if (index == 0) {
// 获取第一行标题
while (cells.hasNext()) {
Cell cell = cells.next(); // 每一格
titleMap.put(cell.getColumnIndex(), cell.getStringCellValue().trim());
}
} else {
// 创建实体对象,把值设置进去
if (!annoMap.isEmpty() && !titleMap.isEmpty()) {
list.add(buildBean(annoMap, titleMap, clazz, cells));
}
}
index++;
}
return list;
}
/**
* 注解名称与字段属性的对应关系
*
* @param clazz 实体对象类类型
* @return Map<String,Field>
*/
private static <T> Map<String, Field> getFields(Class<T> clazz) {
Map<String, Field> annoMap = new HashMap<String, Field>();
Field[] fileds = clazz.getDeclaredFields();
for (Field filed : fileds) {
CellField cellField = filed.getAnnotation(CellField.class);
if (cellField != null && StringUtils.isNotBlank(cellField.name())) {
annoMap.put(cellField.name(), filed);
}
}
return annoMap;
}
/**
* 创建实体,设置值
*
* @param annoMap 注解名称与字段属性的对应的Map
* @param titleMap excel列所引与标题名称对应的Map
* @param clazz 实体对象类类型
* @param cells 每一行的所有格子
* @return List<T>
*/
private static <T> T buildBean(Map<String, Field> annoMap, Map<Integer, String> titleMap, Class<T> clazz, Iterator<Cell> cells) {
T t = null;
try {
t = clazz.newInstance();
while (cells.hasNext()) {
Cell cell = cells.next(); // 每一格
String title = titleMap.get(cell.getColumnIndex());
if (annoMap.containsKey(title)) {
Field field = annoMap.get(title);
Class<?> valType = field.getType();
Object value = getType(cell.getStringCellValue(), valType);
String fieldName = field.getName();
String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Method method = ReflectionUtils.findMethod(clazz, methodName, valType);
if (method != null) {
ReflectionUtils.invokeMethod(method, t, value);
}
}
}
} catch (Exception e) {
LOGGER.error("创建泛型实体对象失败", e);
}
return t;
}
/**
* 转换成实体属性对应的类型
*
* @param value 每一格的数值
* @param valType 实体属性类型
* @return Object 转换为对应类型以obj返回
*/
private static <T> Object getType(String value, Class<T> valType) {
try {
if (valType == String.class) {
return String.valueOf(value);
} else if (valType == Date.class) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 默认格式
return sdf.parse(value);
} else if (valType == Double.class) {
return Double.parseDouble(value);
}else if (valType == BigDecimal.class) {
return new BigDecimal(value);
} else if (valType == Integer.class) {
return Integer.parseInt(value);
} else if (valType == Long.class) {
return Long.parseLong(value);
} else if (valType == Boolean.class) {
return Boolean.parseBoolean(value);
}
} catch (Exception e) {
LOGGER.error("类型转换异常", e);
}
return value;
}
/**
* 创建Workbook对象
*
* @param beans 实体集合对象
* @param fileName 文件名
* @return Workbook
*/
private static <T> Workbook createWorkbook(List<T> beans, String fileName) {
// 1.创建2007版工作簿
Workbook wb = new XSSFWorkbook();
// 2.创建工作表
Sheet sheet = wb.createSheet(fileName);
// 3.获取实体类标有@CellField注解的Field对象
List<Field> fields = getFields(beans);
// 4.设置列宽
for (int i = 0; i < fields.size(); i++) {
sheet.setColumnWidth((short) i, (short) (20.7 * 150));
}
// 5.写入标题
Row row = sheet.createRow(0);
writeTitles(fields, row);
// 6.写入内容
writeContents(beans, fields, sheet);
return wb;
}
/**
* 创建文件对象
*
* @param filePath 保存路径
* @return File
*/
private static File createFile(String filePath) {
File file = null;
try {
// 创建文件目录
if(filePath.contains("/")){
file=new File(filePath.substring(0, filePath.lastIndexOf('/')));
}else if(filePath.contains("\\")){
file= new File(filePath.substring(0, filePath.lastIndexOf('\\')));
}else{
file= new File(Thread.currentThread().getContextClassLoader().getResource("").getPath().substring(1));
}
if (!file.exists()) {
file.mkdirs();
}
// 创建文件路径
file = new File(filePath);
if (!file.exists() && file.createNewFile()) {
LOGGER.info("创建文件对象成功");
}
} catch (IOException e) {
LOGGER.error("创建文件对象失败", e);
}
return file;
}
/**
* 写入标题
*
* @param fields Field对象集合
* @param row 行对象
*/
private static void writeTitles(List<Field> fields, Row row) {
if (fields != null && !fields.isEmpty()) {
for (int i = 0; i < fields.size(); i++) {
CellField cellField = fields.get(i).getAnnotation(CellField.class);
if (cellField == null) {
continue;
}
Cell cell = row.createCell(i);
// 获取带有name属性的值并写入
if (StringUtils.isNotBlank(cellField.name())) {
cell.setCellValue(cellField.name());
}
}
}
}
/**
* 写入内容
*
* @param beans 实体对象集合
* @param fields Field对象集合
* @param sheet 工作表对象
* @param <T> 泛型类
*/
private static <T> void writeContents(List<T> beans, List<Field> fields, Sheet sheet) {
for (int i = 0; i < beans.size(); i++) {
T t = beans.get(i);
Row row = sheet.createRow(i + 1);
for (int j = 0; j < fields.size(); j++) {
CellField cellField = fields.get(j).getAnnotation(CellField.class);
if (cellField != null) {
Class<?> valType = fields.get(j).getType(); // 获取属性类型
String fieldName = fields.get(j).getName(); // 获取属性名
String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Method method = ReflectionUtils.findMethod(t.getClass(), methodName);
setValues(method, t, j, valType, cellField, row); // 设置值
}
}
}
}
/**
* 设置值
*
* @param method 方法对象
* @param t 泛型对象
* @param j 下标
* @param valType 类型对象
* @param cellField 注解对象
* @param row 行对象
* @param <T> 泛型
*/
private static <T> void setValues(Method method, T t, int j, Class<?> valType, CellField cellField, Row row) {
if (method != null) {
Object value = ReflectionUtils.invokeMethod(method, t);
if (value != null && valType == Date.class) {
// 默认日期类型格式:yyyy-MM-dd HH:mm:ss
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
row.createCell(j).setCellValue(sdf.format(((Date) value).getTime()));
} else {
// 字符串
row.createCell(j).setCellValue(value == null ? "" : String.valueOf(value));
}
}
}
/**
* 获取标有@CellField注解的Field对象
*
* @param beans 对象集合
* @return List<Field>
*/
private static <T> List<Field> getFields(List<T> beans) {
Class<? extends Object> cls = beans.get(0).getClass();
Field[] declaredFields = cls.getDeclaredFields();
List<Field> annoFields = new ArrayList<Field>();
// 筛选出标有注解的字段
for (Field field : declaredFields) {
CellField anno = field.getAnnotation(CellField.class);
if (anno != null) {
annoFields.add(field);
}
}
return annoFields;
}
}
4、测试类
package com.dl;
import com.dl.POJO.IPOFileInfo;
import com.dl.Utils.ExcelUtils;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
/**
* excel工具类测试
*/
public class TestExcelUtils {
public static void main(String[] args) throws FileNotFoundException {
List<IPOFileInfo> list = new ArrayList<>();
IPOFileInfo info = new IPOFileInfo();
info.setFileType("1");
info.setFileCode("001");
info.setFileDate("2020-12-26");
info.setFileId("111");
info.setFileName("测试文档1");
info.setFileUrl("www.baidu.com");
list.add(info);
IPOFileInfo info2 = new IPOFileInfo();
info2.setFileType("2");
info2.setFileCode("002");
info2.setFileDate("2020-12-26");
info2.setFileId("222");
info2.setFileName("测试文档2");
info2.setFileUrl("www.baidu.com");
list.add(info2);
IPOFileInfo info3 = new IPOFileInfo();
info3.setFileType("3");
info3.setFileCode("003");
info3.setFileDate("2020-12-26");
info3.setFileId("333");
info3.setFileName("测试文档3");
info3.setFileUrl("www.baidu.com");
list.add(info3);
// 生成excel2007文档
// ExcelUtils.createExcel("E:/5120154230JavaCode/POIAndEasyExcel/EasyExcel/tonghua.xlsx", list);
// 解析excel文档
List<IPOFileInfo> results = ExcelUtils.parseExcel("E:/5120154230JavaCode/POIAndEasyExcel/EasyExcel/tonghua.xlsx", IPOFileInfo.class);
for (IPOFileInfo ipoFileInfo : results) {
System.out.println(ipoFileInfo);
}
}
}
参考文章:java操作excel文档通用工具类