JAVA操作Excel(POI、easyPOI、easyExcel)
POI、easyPOI、easyExcel的使用
我只是讲解最平常的使用技巧,实战中会遇到格式各样的情况,看官方文档、Debug、搜索报错信息,是最适合学习和解决问题的途径,使用官方文档写在了每一条讲解的下方,供大家快速预览
在讲解之前先来聊一下03和07版的Excel
一、支持文档类型不同
1、Excel03:只支持xls类型的文档。
2、Excel07:除了支持xls类型文档,还支持xlsx类型的文档。
二、功能不同
1、Excel03:将智能标记操作与部分电子表格的特定内容关联,并使适当的智能标记操作仅在用户将鼠标悬停在关联的单元格区域上时出现。
2、Excel07:菜单、工具条已经成为历史,取而代之的是功能区,大量图标和命令组织到多个选项卡中,形成带状区域,还有快速访问工具栏、画廊等。
三、特点不同
1、Excel03:使用 Excel 2003 在任何客户定义的 XML 架构中读取数据。在基础 XML 数据存储发生更改时更新图表、表格和曲线图。
2、Excel07:文件都是基于XML格式。新的SmartArt工具,为Office文档创建清晰的过程图、流程图和其他业务相关的图表。
在我们使用代码来操作Excel,03与07版最大的不同就是==2003 版本和 2007 版本存在兼容性的问题!03最多只有 65535 行!==而07版本理论上是没有限制的
POI讲解(基础)
ApachePOI官网
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3YQajOTG-1658855178613)(C:\Users\18358\AppData\Roaming\Typora\typora-user-images\image-20220727001505305.png)]
导包
<!--导入依赖--><dependencies><!--xls(03)--><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.2</version></dependency><!--xlsx(07)--><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.2</version></dependency><!--日期格式化工具--><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>2.10.14</version></dependency><!--test--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
读写操作(03 | 07 版本的写,就是对象不同,方法一样的!)03的对象是 HSSF
;07的对象是 XSSF
写
public class ExcelWriteTest {String PATH = "D:\\idea\\project\\POI_and_easyExcel_study\\01_POI_study\\src\\main\\java\\com\\";//03版本@Testpublic void testWrite03() throws IOException {//1.创建一个工作簿Workbook workbook = new HSSFWorkbook();//2.创建一个工作表Sheet sheet = workbook.createSheet("随便写点");//3、创建一个行(第一行)Row row = sheet.createRow(0);//4、创建一个单元格Cell cell11 = row.createCell(0);cell11.setCellValue("!!!!!!!!!!!!!!");//两个格子填充值Cell cell12 = row.createCell(1);cell12.setCellValue("@@@@@@@@@@@@@@@");//第二行Row row2 = sheet.createRow(1);Cell cell21 = row2.createCell(0);cell21.setCellValue("########################");//(2,2)Cell cell22 = row2.createCell(1);String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");cell22.setCellValue(time);//生成一张表(IO流) 03 版本就是使用xls结尾FileOutputStream fileOutputStream = new FileOutputStream(PATH + "03.xls");workbook.write(fileOutputStream);//关闭流fileOutputStream.close();System.out.println("生成完毕");}//07版本@Testpublic void testWrite2() throws IOException {//1.创建一个工作簿Workbook workbook = new XSSFWorkbook();//2.创建一个工作表Sheet sheet = workbook.createSheet("随便写点");//3、创建一个行(第一行)Row row = sheet.createRow(0);//4、创建一个单元格Cell cell11 = row.createCell(0);cell11.setCellValue("!!!!!!!!!!!!!!");//两个格子填充值Cell cell12 = row.createCell(1);cell12.setCellValue("@@@@@@@@@@@@@@@");//第二行Row row2 = sheet.createRow(1);Cell cell21 = row2.createCell(0);cell21.setCellValue("########################");//(2,2)Cell cell22 = row2.createCell(1);String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");cell22.setCellValue(time);//生成一张表(IO流) 03 版本就是使用xlsx结尾FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07.xlsx");workbook.write(fileOutputStream);//关闭流fileOutputStream.close();System.out.println("生成完毕");}@Testpublic void testWrite03BigData() throws IOException {// 时间差(开始)long begin = System.currentTimeMillis();//创建一个簿Workbook workbook = new HSSFWorkbook();//创建表Sheet sheet = workbook.createSheet();//写入数据for (int rowNum = 0; rowNum < 65536; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellMum = 0; cellMum < 10; cellMum++) {Cell cell = row.createCell(cellMum);cell.setCellValue(cellMum);}}System.out.println("over");FileOutputStream fileOutputStream = new FileOutputStream(PATH + "BigData03.xls");workbook.write(fileOutputStream);fileOutputStream.close();// 时间差(结束)long end = System.currentTimeMillis();System.out.println("结束时间:"+(double)(end-begin)/1000);}//耗时较长@Testpublic void testWrite07BigData() throws IOException {// 时间差(开始)long begin = System.currentTimeMillis();//创建一个簿Workbook workbook = new XSSFWorkbook();//创建表Sheet sheet = workbook.createSheet();//写入数据for (int rowNum = 0; rowNum < 65536; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellMum = 0; cellMum < 10; cellMum++) {Cell cell = row.createCell(cellMum);cell.setCellValue(cellMum);}}System.out.println("over");FileOutputStream fileOutputStream = new FileOutputStream(PATH + "BigData07.xlsx");workbook.write(fileOutputStream);fileOutputStream.close();// 时间差(结束)long end = System.currentTimeMillis();System.out.println("结束时间:"+(double)(end-begin)/1000);}//升级版@Testpublic void testWrite07BigDataS() throws IOException {// 时间差(开始)long begin = System.currentTimeMillis();//创建一个簿SXSSFWorkbook workbook = new SXSSFWorkbook();//创建表Sheet sheet = workbook.createSheet();//写入数据for (int rowNum = 0; rowNum < 65536; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellMum = 0; cellMum < 10; cellMum++) {Cell cell = row.createCell(cellMum);cell.setCellValue(cellMum);}}System.out.println("over");FileOutputStream fileOutputStream = new FileOutputStream(PATH + "BigDataS07.xlsx");workbook.write(fileOutputStream);fileOutputStream.close();//清除临时文件workbook.dispose();// 时间差(结束)long end = System.currentTimeMillis();System.out.println("结束时间:"+(double)(end-begin)/1000);}
}
读
public class ExcelReadTest {String PATH = "D:\\idea\\project\\POI_and_easyExcel_study\\01_POI_study\\src\\main\\java\\com\\";//03版本@Testpublic void testRed03() throws IOException {//获取文件流FileInputStream fileInputStream = new FileInputStream(PATH + "03.xls");//1.创建一个工作簿。 使用excel能操作的这边都能操作!Workbook workbook = new HSSFWorkbook(fileInputStream);//2.得到表Sheet sheetAt = workbook.getSheetAt(0);//3.得到行Row row = sheetAt.getRow(0);//4.得到列Cell cell = row.getCell(0);// 读取值得到时候,一定要注意类型!//getStringCellValue() 获取字符串//getNumericCellValue() 获取数字System.out.println(cell.getStringCellValue());fileInputStream.close();}//07版本@Testpublic void testWrite07() throws IOException {// 创建新的Excel 工作簿, 只有对象变了Workbook workbook = new XSSFWorkbook();// 如要新建一名为"会员登录统计"的工作表,其语句为:Sheet sheet = workbook.createSheet("会员登录统计");// 创建行(row 1)Row row1 = sheet.createRow(0);// 创建单元格(col 1-1)Cell cell11 = row1.createCell(0);cell11.setCellValue("今日新增关注");// 创建单元格(col 1-2)Cell cell12 = row1.createCell(1);cell12.setCellValue(666);// 创建行(row 2)Row row2 = sheet.createRow(1);// 创建单元格(col 2-1)Cell cell21 = row2.createCell(0);cell21.setCellValue("统计时间");//创建单元格(第三列)Cell cell22 = row2.createCell(1);String dateTime = new DateTime().toString("yyyy-MM-dd HH:mm:ss");cell22.setCellValue(dateTime);// 新建一输出文件流(注意:要先创建文件夹)FileOutputStream out = new FileOutputStream(PATH + "07.xlsx");// 把相应的Excel 工作簿存盘workbook.write(out);// 操作结束,关闭文件 out.close();System.out.println("文件生成成功");}//判断类型读取@Testpublic void testCellType() throws IOException{//获取文件流FileInputStream fileInputStream = new FileInputStream(PATH + "03.xls");//1.创建一个工作簿。 使用excel能操作的这边都能操作!Workbook workbook = new HSSFWorkbook(fileInputStream);Sheet sheetAt = workbook.getSheetAt(0);//获取标题内容Row rowTitle = sheetAt.getRow(0);if (rowTitle!=null){// 一点要掌握,获取到一行后,通过循环遍历每一个单元格int cellCount = rowTitle.getPhysicalNumberOfCells();for (int cellNum = 0; cellNum < cellCount; cellNum++) {Cell cell = rowTitle.getCell(cellNum);if (cell!=null){CellType cellType = cell.getCellType();String cellValue = "";switch (cellType){case STRING:System.out.print("【String】");cellValue = cell.getStringCellValue();break;case BOOLEAN:System.out.print("【BOOLEAN】");cellValue = String.valueOf(cell.getBooleanCellValue());break;case BLANK:System.out.print("【BLANK】");break;case NUMERIC: //数字(日期、普通数字)System.out.print("【NUMERIC】");if (DateUtil.isCellDateFormatted(cell)){ //日期System.out.print("【日期】");Date date = cell.getDateCellValue();cellValue = new DateTime(date).toString("yyyy-MM-dd");}else {//不是日期格式,防止数字过长!System.out.print("【数字转化为字符串输出】");cell.setCellType(CellType.STRING);cellValue = cell.toString();}break;case ERROR:System.out.print("【数据类型错误】");break;}System.out.println(cellValue);}}fileInputStream.close();System.out.println();}//获取表中的内容(获取多少行)int rowCount = sheetAt.getPhysicalNumberOfRows();for (int rowNum = 1; rowNum < rowCount; rowNum++) {Row row = sheetAt.getRow(rowNum);if (row!=null){assert rowTitle != null;int cellCount = rowTitle.getPhysicalNumberOfCells();for (int cellNum = 0; cellNum < cellCount; cellNum++) {System.out.print("["+(rowNum+1)+"-"+(cellNum+1)+"]");}}}}
}
计算公式
@Test
public void testFormula() throws Exception{InputStream is = new FileInputStream(path + "计算公式.xls");Workbook workbook = new HSSFWorkbook(is);Sheet sheet = workbook.getSheetAt(0);// 读取第五行第一列Row row = sheet.getRow(4);Cell cell = row.getCell(0);//公式计算器FormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook) workbook);// 输出单元内容int cellType = cell.getCellType();switch (cellType) {case Cell.CELL_TYPE_FORMULA://2//得到公式String formula = cell.getCellFormula();System.out.println(formula);CellValue evaluate = formulaEvaluator.evaluate(cell);//String cellValue = String.valueOf(evaluate.getNumberValue());String cellValue = evaluate.formatAsString();System.out.println(cellValue);break;}
}
easyPOI讲解(推荐)
easyPOI官方文档
悟耘开源 / easypoi(gitee)
导包(普通maven项目)
<dependencies><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.4.0</version></dependency><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-web</artifactId><version>4.4.0</version></dependency><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-annotation</artifactId><version>4.4.0</version></dependency><!-- 我这里用了lombok,根据个人情况选择是否使用,此依赖与easypoi无关,只是许需要写get set方法了--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency></dependencies>
springboot导包
<!--easypoi--><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.4.0</version></dependency><!--下面这是普通maven要到的包-->
<!-- <dependency>-->
<!-- <groupId>cn.afterturn</groupId>-->
<!-- <artifactId>easypoi-base</artifactId>-->
<!-- <version>4.4.0</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>cn.afterturn</groupId>-->
<!-- <artifactId>easypoi-web</artifactId>-->
<!-- <version>4.4.0</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>cn.afterturn</groupId>-->
<!-- <artifactId>easypoi-annotation</artifactId>-->
<!-- <version>4.4.0</version>-->
<!-- </dependency>-->
我写了两个测试导入导出的Demo,但这两个Demo操作的不是同一个文件,需要自行修改,后面有实战,使用了工具类,比这两个测试Demo都要简单
测试Demo1(导入)
实体类(下面有专门对注解的解释)
StopWatchExport.java
/*** @Role 码表导出实体类* @Author 赵平安* @GMTCreate 2022年7月22日* @Modifier* @GMTModified* @ModifyContent*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StopWatchExport {/*** 码表编号*/@Excel(name="码表编号", width = 30,needMerge = true)private String sCode;/*** 码表名称*/@Excel(name="码表名称", width = 30,needMerge = true)private String sName;/*** 码表说明*/@Excel(name="码表说明", width = 30,needMerge = true)private String sComment;/*** 码表状态*/@Excel(name="状态", replace = { "未发布_0","已发布_1", "已停用_2" }, width = 30,needMerge = true)private String sStatus;/*** 更新时间*/@Excel(name="更新时间", width = 30, databaseFormat = "yyyyMMddHHmmss", format = "yyyy-MM-dd HH:mm:ss", isImportField = "true_st",needMerge = true)private Date updateTime;@ExcelCollection(name = "码表码值")private List<StopWatchValueExport> stopWatchValueExportList;}
StopWatchValueExport.java
/*** @Role 码值导出实体类* @Author 赵平安* @GMTCreate 2022年7月22日* @Modifier* @GMTModified* @ModifyContent*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StopWatchValueExport {/*** 码值取值*/@Excel(name = "码值取值", width = 30)private String cValue;/*** 码值名称*/@Excel(name = "码值名称", width = 30)private String cName;/*** 码值含义*/@Excel(name = "码值含义", width = 30)private String cComment;}
mytest.java
public class Mytest {@Testpublic void haveTitleTest() {String PATH = "D:\\idea\\project\\POI_and_easyExcel_study\\easyPOI\\src\\main\\java\\com\\";ImportParams params = new ImportParams();//设置标题的行数,有标题时一定要有params.setTitleRows(1);//设置表头的行数params.setHeadRows(2);String file = PATH+"123.xls";List<StopWatchExport> list = ExcelImportUtil.importExcel(new File(file),StopWatchExport.class, params);System.out.println("解析到的数据长度是:" + list.size());for (StopWatchExport scoreIssueReqPOJO : list) {System.out.println("***********有标题有表头导入的数据是=" + scoreIssueReqPOJO.toString());}}
}
测试Demo2(导出)
实体类
Employee.java
public class Employee {/*** id*/private String id;/*** 员工姓名*/@Excel(name = "员工姓名", width = 30,needMerge = true)private String name;/*** 员工性别*/@Excel(name = "员工性别", replace = { "男_1", "女_2" }, suffix = "生", isImportField = "true_st",needMerge = true)private int sex;@Excel(name = "开始日期", databaseFormat = "yyyyMMddHHmmss", format = "yyyy-MM-dd", isImportField = "true_st", width = 20,needMerge = true)private Date birthday;@Excel(name = "完工日期", databaseFormat = "yyyyMMddHHmmss", format = "yyyy-MM-dd",needMerge = true)private Date registrationDate;@ExcelCollection(name = "报销金额")private List<BaoXiao> baoxiao;}
BaoXiao.java
public class BaoXiao {/*** 火车票金额*/@Excel(name = "火车票金额", width = 30)private String hcpje;/*** 汽车票金额*/@Excel(name = "火车票金额", width = 30)private String qcpje;public BaoXiao(String hcpje, String qcpje) {this.hcpje = hcpje;this.qcpje = qcpje;}
}
Demo
public class Demo {String PATH = "D:\\idea\\project\\POI_and_easyExcel_study\\easyPOI\\src\\main\\java\\com\\";@Testpublic void fun(){try {List<BaoXiao> xiaos = new ArrayList<>();BaoXiao baoXiao1 = new BaoXiao("100","200");BaoXiao baoXiao2 = new BaoXiao("110","210");BaoXiao baoXiao3 = new BaoXiao("120","220");BaoXiao baoXiao4 = new BaoXiao("130","230");xiaos.add(baoXiao1);xiaos.add(baoXiao2);xiaos.add(baoXiao3);xiaos.add(baoXiao4);List<Employee> list = new ArrayList<>();Employee s1 = new Employee("001","张三",1,new Date(),new Date(),xiaos);Employee s2 = new Employee("002","李四",2,new Date(),new Date(),xiaos);Employee s3 = new Employee("003","王五",1,new Date(),new Date(),xiaos);Employee s4 = new Employee("004","赵六",2,new Date(),new Date(),xiaos);list.add(s1);list.add(s2);list.add(s3);list.add(s4);list.forEach(System.out::println);Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("员工差旅报销金额","学生"),Employee.class, list);FileOutputStream outputStream = new FileOutputStream(PATH+"123.xls");workbook.write(outputStream);outputStream.close();} catch (Exception e) {e.printStackTrace();}}
}
效果图
实战
实体类(下面有专门对注解的解释)
StopWatchExport.java
/*** @Role 码表导出实体类* @Author 赵平安* @GMTCreate 2022年7月22日* @Modifier* @GMTModified* @ModifyContent*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StopWatchExport {/*** 码表编号*/@Excel(name="码表编号", width = 30,needMerge = true)private String sCode;/*** 码表名称*/@Excel(name="码表名称", width = 30,needMerge = true)private String sName;/*** 码表说明*/@Excel(name="码表说明", width = 30,needMerge = true)private String sComment;/*** 码表状态*/@Excel(name="状态", replace = { "未发布_0","已发布_1", "已停用_2" }, width = 30,needMerge = true)private String sStatus;/*** 更新时间*/@Excel(name="更新时间", width = 30, databaseFormat = "yyyyMMddHHmmss", format = "yyyy-MM-dd HH:mm:ss", isImportField = "true_st",needMerge = true)private Date updateTime;@ExcelCollection(name = "码表码值")private List<StopWatchValueExport> stopWatchValueExportList;}
StopWatchValueExport.java
/*** @Role 码值导出实体类* @Author 赵平安* @GMTCreate 2022年7月22日* @Modifier* @GMTModified* @ModifyContent*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StopWatchValueExport {/*** 码值取值*/@Excel(name = "码值取值", width = 30)private String cValue;/*** 码值名称*/@Excel(name = "码值名称", width = 30)private String cName;/*** 码值含义*/@Excel(name = "码值含义", width = 30)private String cComment;}
工具类
/*** @Title: EasyPoiUtil* @ProjectName* @Description:*/
public class EasyPoiUtil {/*** 导出excel** @param list 数据list* @param title 标题* @param sheetName sheet名称* @param pojoClass 实体的class* @param fileName 文件名称* @param isCreateHeader 是否创建头* @param response 响应*/public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response) {ExportParams exportParams = new ExportParams(title, sheetName);exportParams.setCreateHeadRows(isCreateHeader);defaultExport(list, pojoClass, fileName, response, exportParams);}/*** 导出excel** @param list 数据list* @param title 标题* @param sheetName sheet名称* @param pojoClass 实体class* @param fileName 文件名* @param response 响应*/public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=GBK");try {response.setHeader("content-disposition","attachment;filename=" + java.net.URLEncoder.encode(fileName, "GBK")+ ";filename*=GBK''" + java.net.URLEncoder.encode(fileName, "GBK"));} catch (UnsupportedEncodingException e) {// ...}defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName));}/*** 导出excel** @param list 多个Map key title 对应表格Title key entity 对应表格对应实体 key data* @param fileName 标题* @param response 响应*/public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) {defaultExport(list, fileName, response);}/*** 导出多个excel** @param workbooks 多个excel文件 通过ExcelExportUtil.exportExcel往workbooks内放入excel* @param fileNames 文件名 每个excel文件的名称顺序必须一致且名称请务必保证不重复* @param fileName 压缩包文件名* @param response 标题*/public static void exportExcels(List<Workbook> workbooks, List<String> fileNames, String fileName, HttpServletResponse response) {try {response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".zip");OutputStream toClient = new BufferedOutputStream(response.getOutputStream());ZipOutputStream zipOut = new ZipOutputStream(toClient);for (int i = 0; i < workbooks.size(); i++) {ZipEntry entry = new ZipEntry(fileNames.get(i) + ".xls");zipOut.putNextEntry(entry);workbooks.get(i).write(zipOut);}zipOut.flush();zipOut.close();} catch (IOException e) {throw new ExcelExportException(e.getMessage());}}private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) {Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);if (workbook != null) {;}downLoadExcel(fileName, response, workbook);}private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) {try {response.setCharacterEncoding("UTF-8");response.setHeader("content-Type", "application/vnd.ms-excel");response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));workbook.write(response.getOutputStream());} catch (IOException e) {throw new ExcelImportException(e.getMessage());}}private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) {Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);if (workbook != null) {;}downLoadExcel(fileName, response, workbook);}public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) {if (StringUtils.isBlank(filePath)) {return null;}ImportParams params = new ImportParams();params.setTitleRows(titleRows);params.setHeadRows(headerRows);List<T> list = null;try {list = ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);} catch (NoSuchElementException e) {throw new ExcelImportException("模板不能为空");} catch (Exception e) {e.printStackTrace();throw new ExcelImportException(e.getMessage());}return list;}public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) {if (file == null) {return null;}ImportParams params = new ImportParams();params.setTitleRows(titleRows);params.setHeadRows(headerRows);List<T> list = null;try {list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params);} catch (NoSuchElementException e) {throw new ExcelImportException("excel文件不能为空");} catch (Exception e) {throw new ExcelImportException(e.getMessage());}return list;}
}
导入导出操作
controller
/*** @Role 导出码表* @Author 赵平安* @GMTCreate 2022年7月22日* @Modifier* @GMTModified* @ModifyContent*/@PostMapping("/exportExcel")@ApiOperation("导出Excel")public void exportExcel(HttpServletResponse response){List<StopWatchExport> stopWatchExports = iStopWatchService.StopWatchExport();EasyPoiUtil.exportExcel(stopWatchExports,"码表","码表",StopWatchExport.class,"码表.xlsx",response);}/*** @Role 导入码表* @Author 赵平安* @GMTCreate 2022年7月22日* @Modifier* @GMTModified* @ModifyContent*/@PostMapping("/importExcel")@ApiOperation("导入Excel")public R importExcel(MultipartFile excelFile){try {// 参数过滤if (excelFile == null){return R.Failed("文件不能为空!");}else {List<StopWatchExport> stopWatchExports = EasyPoiUtil.importExcel(excelFile, 1, 2, StopWatchExport.class);iStopWatchService.importExcel(stopWatchExports);}// 调用服务处理业务} catch (Exception e) {return R.Failed("未知错误!");}return R.Success();}
service
/*** @Role 码表导出* @Author 赵平安* @GMTCreate 2022年7月22日* @Modifier* @GMTModified* @ModifyContent*/@Overridepublic List<StopWatchExport> StopWatchExport() {List<StopWatchExport> stopWatchExports = stopWatchMapper.StopWatchExport();return stopWatchExports;}/*** @Role 导入码表* @Author 赵平安* @GMTCreate 2022年7月22日* @Modifier* @GMTModified* @ModifyContent*/@Overridepublic int importExcel(List<StopWatchExport> stopWatchExports) {AtomicInteger insert = new AtomicInteger(0);stopWatchExports.forEach(stopWatchExport -> {List<StopWatchValueExport> stopWatchValueExportList = stopWatchExport.getStopWatchValueExportList();System.out.println(stopWatchValueExportList);StopWatch stopWatch = new StopWatch();stopWatch.setsName(stopWatchExport.getSName());stopWatch.setsCode(stopWatchExport.getSCode());stopWatch.setsComment(stopWatchExport.getSComment());int i = stopWatchMapper.insert(stopWatch);insert.set(i);StopWatchValue stopWatchValue = new StopWatchValue();stopWatchValue.setsId(stopWatch.getsId());stopWatchValueExportList.forEach(stopWatchValueExport -> {stopWatchValue.setcValue(stopWatchValueExport.getCValue());stopWatchValue.setcComment(stopWatchValueExport.getCComment());stopWatchValue.setcName(stopWatchValueExport.getCName());stopWatchValue.setcId(null);System.out.println(stopWatchValue);int i2 = stopWatchValueMapper.insert(stopWatchValue);insert.set(i2);});});return insert.get();}
}
注解
注解介绍
easypoi起因就是Excel的导入导出,最初的模板是实体和Excel的对应,model–row,filed–col 这样利用注解我们可以和容易做到excel到导入导出 经过一段时间发展,现在注解有5个类分别是
@Excel 作用到filed上面,是对Excel一列的一个描述
@ExcelCollection 表示一个集合,主要针对一对多的导出,比如一个老师对应多个科目,科目就可以用集合表示
@ExcelEntity 表示一个继续深入导出的实体,但他没有太多的实际意义,只是告诉系统这个对象里面同样有导出的字段
@ExcelIgnore 和名字一样表示这个字段被忽略跳过这个导导出
@ExcelTarget 这个是作用于最外层的对象,描述这个对象的id,以便支持一个对象可以针对不同导出做出不同处理
@Excel
这个是必须使用的注解,如果需求简单只使用这一个注解也是可以的,涵盖了常用的Excel需求,需要大家熟悉这个功能,主要分为基础,图片处理,时间处理,合并处理几块,name_id是上面讲的id用法,这里就不累言了
属性 | 类型 | 默认值 | 功能 |
---|---|---|---|
name | String | null | 列名,支持name_id |
needMerge | boolean | fasle | 是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row) |
orderNum | String | “0” | 列的排序,支持name_id |
replace | String[] | {} | 值得替换 导出是{a_id,b_id} 导入反过来 |
savePath | String | “upload” | 导入文件保存路径,如果是图片可以填写,默认是upload/className/ IconEntity这个类对应的就是upload/Icon/ |
type | int | 1 | 导出类型 1 是文本 2 是图片,3 是函数,10 是数字 默认是文本 |
width | double | 10 | 列宽 |
height | double | 10 | 列高,后期打算统一使用@ExcelTarget的height,这个会被废弃,注意 |
isStatistics | boolean | fasle | 自动统计数据,在追加一行统计,把所有数据都和输出 这个处理会吞没异常,请注意这一点 |
isHyperlink | boolean | false | 超链接,如果是需要实现接口返回对象 |
isImportField | boolean | true | 校验字段,看看这个字段是不是导入的Excel中有,如果没有说明是错误的Excel,读取失败,支持name_id |
exportFormat | String | “” | 导出的时间格式,以这个是否为空来判断是否需要格式化日期 |
importFormat | String | “” | 导入的时间格式,以这个是否为空来判断是否需要格式化日期 |
format | String | “” | 时间格式,相当于同时设置了exportFormat 和 importFormat |
databaseFormat | String | “yyyyMMddHHmmss” | 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式,用以转换时间格式输出 |
numFormat | String | “” | 数字格式化,参数是Pattern,使用的对象是DecimalFormat |
imageType | int | 1 | 导出类型 1 从file读取 2 是从数据库中读取 默认是文件 同样导入也是一样的 |
suffix | String | “” | 文字后缀,如% 90 变成90% |
isWrap | boolean | true | 是否换行 即支持\n |
mergeRely | int[] | {} | 合并单元格依赖关系,比如第二列合并是基于第一列 则{0}就可以了 |
mergeVertical | boolean | fasle | 纵向合并内容相同的单元格 |
fixedIndex | int | -1 | 对应excel的列,忽略名字 |
isColumnHidden | boolean | false | 导出隐藏列 |
@ExcelTarget
限定一个到处实体的注解,以及一些通用设置,作用于最外面的实体
属性 | 类型 | 默认值 | 功能 |
---|---|---|---|
value | String | null | 定义ID |
height | double | 10 | 设置行高 |
fontSize | short | 11 | 设置文字大小 |
@ExcelEntity
标记是不是导出excel 标记为实体类,一遍是一个内部属性类,标记是否继续穿透,可以自定义内部id
属性 | 类型 | 默认值 | 功能 |
---|---|---|---|
id | String | null | 定义ID |
@ExcelCollection
一对多的集合注解,用以标记集合是否被数据以及集合的整体排序
属性 | 类型 | 默认值 | 功能 |
---|---|---|---|
id | String | null | 定义ID |
name | String | null | 定义集合列名,支持nanm_id |
orderNum | int | 0 | 排序,支持name_id |
type | Class<?> | ArrayList.class | 导入时创建对象使用 |
@ExcelIgnore
忽略这个属性,多使用需循环引用中
easyExcel讲解(他的官方文档写的很详细,比我写的好得多,如果想学easyExcel只需要看官方文档就行了,也可以跟着我的步骤走,配完一步到位)
easyExcel官方文档
导包
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></dependency><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>2.10.14</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.80</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>RELEASE</version><scope>compile</scope></dependency>
</dependencies>
写入测试
1、DemoData.java
@Getter
@Setter
@EqualsAndHashCode
public class DemoData {@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ExcelProperty("数字标题")private Double doubleData;/*** 忽略这个字段*/@ExcelIgnoreprivate String ignore;
}
2、读操作的时候要写个监听器
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;/*** 缓存的数据*/private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);/*** 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。*/private DemoDAO demoDAO;public DemoDataListener() {// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数demoDAO = new DemoDAO();}/*** 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来** @param demoDAO*/public DemoDataListener(DemoDAO demoDAO) {this.demoDAO = demoDAO;}/*** 这个每一条数据解析都会来调用** @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/// 读取数据会执行invoke方法// DemoData 类型// AnalysisContext 分析上下文@Overridepublic void invoke(DemoData data, AnalysisContext context) {System.out.println("解析到一条数据:{}"+JSON.toJSONString(data));cachedDataList.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (cachedDataList.size() >= BATCH_COUNT) {saveData(); //持久化逻辑!// 存储完成清理 listcachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();log.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {log.info("{}条数据,开始存储数据库!", cachedDataList.size());demoDAO.save(cachedDataList);log.info("存储数据库成功!");}
}
3、持久层(也是读操作需要的)
/*** 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。**/
public class DemoDAO {public void save(List<DemoData> list) {// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入}
}
4、Demo.java
public class Demo {private List<DemoData> data() {List<DemoData> list = ListUtils.newArrayList();for (int i = 0; i < 10; i++) {DemoData data = new DemoData();
// data.setString("字符串" + i);
// data.setDate(new Date());
// data.setDoubleData(0.56);list.add(data);}return list;}//根据list 写入excel/*** 最简单的写* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 直接写即可*/String PATH = "D:\\idea\\project\\POI_and_easyExcel_study\\02_easyExcel_study\\src\\main\\java\\com\\zhao\\";@Testpublic void simpleWrite() {// 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入// 写法1 JDK8+// since: 3.0.0-beta1String fileName = PATH+"EasyTest.xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 如果这里想使用03 则 传入excelType参数即可EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(() -> {// 分页查询数据return data();});// 写法2fileName = PATH+"EasyTest2.xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 如果这里想使用03 则 传入excelType参数即可EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());// 写法3fileName = PATH+"EasyTest3.xlsx";// 这里 需要指定写用哪个class去写try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) {WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();excelWriter.write(data(), writeSheet);}}/*** 最简单的读* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}* <p>* 3. 直接读即可*/@Testpublic void simpleRead() {// 写法1:JDK8+ ,不用额外写一个DemoDataListener// since: 3.0.0-beta1String fileName = PATH+"EasyTest.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭// 这里每次会读取100条数据 然后返回过来 直接调用使用数据就行EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {for (DemoData demoData : dataList) {System.out.println("读取到一条数据{}"+JSON.toJSONString(demoData));}})).sheet().doRead();// 写法2:// 匿名内部类 不用额外写一个DemoDataListenerfileName = PATH+"EasyTest.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(fileName, DemoData.class, new ReadListener<DemoData>() {/*** 单次缓存的数据量*/public static final int BATCH_COUNT = 100;/***临时存储*/private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);@Overridepublic void invoke(DemoData data, AnalysisContext context) {cachedDataList.add(data);if (cachedDataList.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listcachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {saveData();}/*** 加上存储数据库*/private void saveData() {System.out.println("{}条数据,开始存储数据库!"+cachedDataList.size());System.out.println("存储数据库成功!");}}).sheet().doRead();// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去// 写法3:String fileName = PATH+"EasyTest.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();// 写法4fileName = PATH+"EasyTest.xlsx";// 一个文件一个readertry (ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build()) {// 构建一个sheet 这里可以指定名字或者noReadSheet readSheet = EasyExcel.readSheet(0).build();// 读取一个sheetexcelReader.read(readSheet);}}
}
参考:EasyPOI 教程以及完整工具类的使用_HiBoyljw的博客-CSDN博客_easypoi
参考:easypoi一对多导出excel_jattxgt的博客-CSDN博客_easypoi导出excel一对多
参考:【狂神说Java】POI及EasyExcel一小时搞定通俗易懂
JAVA操作Excel(POI、easyPOI、easyExcel)相关推荐
- Java操作excel(POI、EasyExcel)
Apache POI和EasyExcel学习 easyExcel easyExcel 官网地址:https://www.yuque.com/easyexcel/doc/easyexcel EasyEx ...
- java excel api 下载文件_Java-Excel Java操作Excel POI(Jakarta POI API) - 下载 - 搜珍网
Java操作Excel/Jakarta POI API/data/Jakarta POI API.doc Java操作Excel/Jakarta POI API/jar/poi-3.0.2-FINAL ...
- Java操作Excel报表,EasyExcel用法大全
一:EasyExcel简介 1.EasyExcel是一个基于Java的简单.省内存的读写Excel的开源项目.在尽可能节约内存的情况下支持读写百M的Excel. 2.Java解析.生成Excel比较有 ...
- JAVA操作Excel之阿里巴巴EasyExcel
EasyExcel JAVA解析Excel工具 POI非常耗内存,会出现内存溢出 以下是EasyExcel和POI解析Excel的对比图 导入依赖 <dependency><grou ...
- Java操作Excel导入导出(EasyExcel)
在管理一个系统时,总会有许多的数据,为了方便浏览查看数据,系统总会提供「导出Excel」的功能:有导出就有导入,在要向数据库中插入大量的数据时,我们向程序提供准备好的 Excel,然后程序读取表格内容 ...
- Java 操作Excel POI
对指定的单元格进行填充具体的值: public static boolean setValuebySheetRowColumn(Sheet OneSheet, int row, int column, ...
- Java操作Excel三种方式POI、Hutool、EasyExcel
Java操作Excel三种方式POI.Hutool.EasyExcel 1. Java操作Excel概述 1.1 Excel需求概述 1.2 Excel操作三种方式对比 2. ApachePOIExc ...
- java excel读取操作,Java 操作 Excel (读取Excel2003 2007,Poi兑现)
Java 操作 Excel (读取Excel2003 2007,Poi实现) 一. Apache POI 简介( http://poi.apache.org/) 使用Java程序读写Microsoft ...
- Java操作Excel之POI:java读写excel文件以及打印设置
Java操作Excel之POI:java读写excel文件以及打印设置 POI的jar包下载地址:http://poi.apache.org/download.html 注意:项目中导入poi 4.0 ...
- java insert row,POI ,Java 操作 Excel 实现行的插入(insert row)
POI ,Java 操作 Excel 实现行的插入(insert row) 前几天,正在做一个项目,主要用 POI 来操作 Excel 其中,要使用一个,插入功能.主要是因为从数据库,返回结果集(数据 ...
最新文章
- wordpress漏洞_聊聊 WordPress 5.1.1 CSRF to RCE 漏洞
- 利用IPFS构建自己的去中心化分布式Wiki系统
- 根据上边栏和下边栏的高度进行布局
- 如何优化代码节约系统资源解决重复实例化对象的问题——神奇的单例模式(C#设计模式)...
- SPI驱动0.96/1.3寸 OLED屏幕,易修改为DMA控制
- 如何快速掌握python包_如何快速掌握一个python模块?
- xmpp协议抓包_抓包工具有哪些?大佬们常用的18款抓包工具就是这些
- Php 中如何将内容写入log日志中
- HTML5模板生成工具
- 数据--第41棵 - 图的存储结构
- pane Java_ToolPane.java
- python字体类型arial_python 更改字体_更改字体名称而不更改默认字体python
- CGLIB 动态代理使用
- ansys18安装以后打不开_ansys18.0安装过程及常见问题解决方案【图文】
- SpringBoot集成Shiro管理会话和权限
- 传感器与检测技术考试
- 计算机硬件系统概念,计算机系统概念
- Golang lua交互——gopher-lua中call函数使用
- 多邻国-英语学习笔记
- nginx配置解析之缓冲区管理
热门文章
- 本人博客目录 [实时更新]
- 详解WAF与极风云WAF
- 【从零开始学极狐gitlab】01环境搭建 #JIHULAB101
- 弗洛伊德:本我、自我和超我
- 计算机设备选型原则,计算机中通信技术的选用应遵循什么原则
- 1602液晶指定位置光标闪烁程序
- IT人论房价 (七) 城市置业乱谈
- UCenter的百科
- python if实现对话_Python生成微信对话生成器(四)
- AAAI-2021-RE-Progressive Multitask Learning with Controlled Information Flow for Joint Entity and Re