Java操作Excel
一、小册子(原生POI)
gitee地址:https://gitee.com/zhang-zhixi/springboot-poi-excel.git
GitHub地址:https://github.com/zhangzhixi0305/springboot-poi-excel.git
1、新建SpringBoot项目导入Pom
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <dependencies> <!--对Excel操作使用到的jar包--> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.1</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional> true </optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> </dependencies> |
2、进行上传Excel的表单数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <! DOCTYPE html> < html lang="en"> < head > < meta charset="UTF-8"> < title >Title</ title > </ head > < body > < form action="/importExcel" method="post" enctype="multipart/form-data"> 选择文件:< input type="file" name="file"/>< br > < input type="submit" value="上传"/> </ form > </ body > </ html > |
3、处理Excel上传下载请求的Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | package com.zhixi.controller; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.*; /** * @author zhangzhixi * @version 1.0 * @date 2021-12-28 16:36 */ @RestController public class ExcelController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @GetMapping("/exportExcel") public void exportExcel(HttpServletResponse response) throws Exception { // 模拟从数据库查询数据 List< Student > studentList = new ArrayList<>(); studentList.add(new Student(1L, "周深(web导出)", 28, "贵州", new SimpleDateFormat("yyyy-MM-dd").parse("1992-9-29"), 161.0, true)); studentList.add(new Student(2L, "李健(web导出)", 46, "哈尔滨", new SimpleDateFormat("yyyy-MM-dd").parse("1974-9-23"), 174.5, true)); studentList.add(new Student(3L, "周星驰(web导出)", 58, "香港", new SimpleDateFormat("yyyy-MM-dd").parse("1962-6-22"), 174.0, false)); // 读取模板(实际开发可以放在resources文件夹下,随着项目一起打包发布) InputStream excelInputStream = new ClassPathResource("static/excel/student_info.xlsx").getInputStream(); // XSSFWorkbook除了直接接收Path外,还可以传入输入流 XSSFWorkbook workbook = new XSSFWorkbook(excelInputStream); // 获取模板sheet XSSFSheet sheet = workbook.getSheetAt(0); // 找到数据起始行(前两行是标题和表头,要跳过,所以是getRow(2)) XSSFRow dataTemplateRow = sheet.getRow(2); // 构造一个CellStyle数组,用来存放单元格样式。一行有N个单元格,数组初始长度就设置为N CellStyle[] cellStyles = new CellStyle[dataTemplateRow.getLastCellNum()]; for (int i = 0; i < cellStyles.length ; i++) { // 收集每一个格子对应的格式,你可以理解为准备了一把“格式刷” cellStyles[i] = dataTemplateRow.getCell(i).getCellStyle(); } // 创建单元格,并设置样式和数据 for (int i = 0; i < studentList.size(); i++) { // 注意是i+2,模板前两行是大标题和表头。你可能看着难受,想把上面for的i改为i+2,千万别。因为studentList必须从0开始取值 XSSFRow row = sheet.createRow(i + 2); // 为每一行创建单元格并设置数据 Student student = studentList.get(i); XSSFCell nameCell = row.createCell(0);// 创建单元格 nameCell.setCellValue(student.getName()); // 设置值 nameCell.setCellStyle(cellStyles[0]); // 设置单元格样式 XSSFCell ageCell = row.createCell(1); ageCell.setCellValue(student.getAge()); ageCell.setCellStyle(cellStyles[1]); XSSFCell addressCell = row.createCell(2); addressCell.setCellValue(student.getAddress()); addressCell.setCellStyle(cellStyles[2]); /** * 你可能有疑问,这里是日期类型,是不是要和上一次一样,设置单元格样式为日期类型? * 这回不用了,因为上面已经拷贝了模板的样式,生日一栏就是按日期类型展示的 */ XSSFCell birthdayCell = row.createCell(3); birthdayCell.setCellValue(student.getBirthday()); birthdayCell.setCellStyle(cellStyles[3]); XSSFCell heightCell = row.createCell(4); heightCell.setCellValue(student.getHeight()); heightCell.setCellStyle(cellStyles[4]); XSSFCell mainLandChinaCell = row.createCell(5); mainLandChinaCell.setCellValue(student.getIsMainlandChina()); mainLandChinaCell.setCellStyle(cellStyles[5]); } /** * 之前通过本地文件流输出到桌面: * FileOutputStream out = new FileOutputStream("/Users/kevin/Documents/study/student_info_export.xlsx"); * 现在用网络流:response.getOutputStream() * 注意,response的响应流没必要手动关闭,交给Tomcat关闭 */ String fileName = new String("学生信息表.xlsx".getBytes("UTF-8"), "ISO-8859-1"); response.setContentType("application/octet-stream"); response.setHeader("content-disposition", "attachment;filename=" + fileName); response.setHeader("filename", fileName); workbook.write(response.getOutputStream()); workbook.close(); logger.info("导出学生信息表成功!"); } @PostMapping("/importExcel") public Map importExcel(MultipartFile file) throws Exception { // 直接获取上传的文件流,传入构造函数 XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream()); // 获取工作表。一个工作薄中可能有多个工作表,比如sheet1 sheet2,可以根据下标,也可以根据sheet名称。这里根据下标即可。 XSSFSheet sheet = workbook.getSheetAt(0); // 收集每一行数据(跳过标题和表头,所以int i = 2) int lastRowNum = sheet.getLastRowNum(); List<Student> studentList = new ArrayList<>(); for (int i = 2; i <= lastRowNum; i++) { // 收集当前行所有单元格的数据 XSSFRow row = sheet.getRow(i); short lastCellNum = row.getLastCellNum(); List< String > cellDataList = new ArrayList<>(); for (int j = 0; j < lastCellNum ; j++) { cellDataList.add(getValue(row.getCell(j))); } // 把当前行数据设置到POJO。由于Excel单元格的顺序和POJO字段顺序一致,也就是数据类型一致,所以可以直接强转 Student student = new Student(); student.setName(cellDataList.get(0)); student.setAge(Integer.parseInt(cellDataList.get(1))); student.setAddress(cellDataList.get(2)); // getValue()方法返回的是字符串类型的 1962-6-22 00:00:00,这里按"yyyy-MM-dd HH:mm:ss"重新解析为Date student.setBirthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(cellDataList.get(3))); student.setHeight(Double.parseDouble(cellDataList.get(4))); student.setHeight(Double.parseDouble(cellDataList.get(4))); student.setIsMainlandChina(Boolean.valueOf(cellDataList.get(5))); studentList.add(student); } // 插入数据库 saveToDB(studentList); logger.info("导入{}成功!", file.getOriginalFilename()); Map<String, Object> result = new HashMap<>(); result.put("code", 200); result.put("data", null); result.put("msg", "success"); return result; } private void saveToDB(List< Student > studentList) { if (CollectionUtils.isEmpty(studentList)) { return; } // 直接打印,模拟插入数据库 studentList.forEach(System.out::println); } /** * 提供POI数据类型 --> Java数据类型的转换 * 由于本方法返回值设为String,所以不管转换后是什么Java类型,都要以String格式返回 * 所以Date会被格式化为yyyy-MM-dd HH:mm:ss * 后面根据需要自己另外转换 * * @param cell * @return */ private String getValue(Cell cell) { if (cell == null) { return ""; } switch (cell.getCellType()) { case STRING: return cell.getRichStringCellValue().getString().trim(); case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { // DateUtil是POI内部提供的日期工具类,可以把原本是日期类型的NUMERIC转为Java的Data类型 Date javaDate = DateUtil.getJavaDate(cell.getNumericCellValue()); String dateString = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(javaDate); return dateString; } else { /* * 无论Excel中是58还是58.0,数值类型在POI中最终都被解读为Double。 * 这里的解决办法是通过BigDecimal先把Double先转成字符串,如果是.0结尾,把.0去掉 * */ String strCell = ""; Double num = cell.getNumericCellValue(); BigDecimal bd = new BigDecimal(num.toString()); if (bd != null) { strCell = bd.toPlainString(); } // 去除 浮点型 自动加的 .0 if (strCell.endsWith(".0")) { strCell = strCell.substring(0, strCell.indexOf(".")); } return strCell; } case BOOLEAN: return String.valueOf(cell.getBooleanCellValue()); default: return ""; } } @Data @NoArgsConstructor @AllArgsConstructor static class Student { private Long id; private String name; private Integer age; private String address; private Date birthday; private Double height; private Boolean isMainlandChina; } } |
二、狂神说Java(原生POI)
读操作用到的Excel表:会员消费商品明细表
1、创建一个普通的Java项目,导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <dependencies> <!--原生POI依赖--> <!--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> <!--test--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> |
2、简要说明
HSSFWorkbook :对应 Excel 03 版本,最多支持65535行
XSSFWorkbook:对应 Excel 07 版本,行数无限制
SXSSFWorkbook:缓存版本的操作Excel方式。
03版最多支持到65536行,而07版不受限制,理论上无限
3、案例测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.Test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; /** * @ClassName ExcelWriter * @Author zhangzhixi * @Description POI操作Excel * @Date 2022/8/29 13:06 * @Version 1.0 */ public class ExcelWriterReadTest { /** * 将Excel文件写入到什么地方 */ public final static String WRITER_PATH = "F:\\StudyProject\\狂神说\\Excel操作\\zhjixi-poi\\src\\main\\resources\\" ; /** * 写入Excel-03版本 */ @Test public void excelTarget03() { // 1、创建工作簿 Workbook workbook = new HSSFWorkbook(); // 2、创建工作表 Sheet sheet = workbook.createSheet( "考核成绩表" ); // 3、创建一行 Row row1 = sheet.createRow( 0 ); // 4、创建一个列(一个单元格) Cell cell11 = row1.createCell( 0 ); cell11.setCellValue( "语文" ); Cell cell12 = row1.createCell( 1 ); cell12.setCellValue( "数学" ); Cell cell13 = row1.createCell( 2 ); cell13.setCellValue( "英语" ); // 创建一行 Row row2 = sheet.createRow( 1 ); Cell cell21 = row2.createCell( 0 ); Cell cell22 = row2.createCell( 1 ); Cell cell23 = row2.createCell( 2 ); cell21.setCellValue( "75" ); cell22.setCellValue( "70" ); cell23.setCellValue( "66" ); // 5、生成工作簿 FileOutputStream fileOutputStream = null ; try { fileOutputStream = new FileOutputStream(WRITER_PATH + "考核成绩表03.xls" ); workbook.write(fileOutputStream); System.out.println( "工作表生成成功!" ); } catch (Exception e) { e.printStackTrace(); } finally { try { workbook.close(); assert fileOutputStream != null ; fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 写入Excel-07版本 */ @Test public void excelTarget07() { // 1、创建工作簿 Workbook workbook = new XSSFWorkbook(); // 2、创建工作表 Sheet sheet = workbook.createSheet( "考核成绩表" ); // 3、创建一行 Row row1 = sheet.createRow( 0 ); // 4、创建一个列(一个单元格) Cell cell11 = row1.createCell( 0 ); cell11.setCellValue( "语文" ); Cell cell12 = row1.createCell( 1 ); cell12.setCellValue( "数学" ); Cell cell13 = row1.createCell( 2 ); cell13.setCellValue( "英语" ); // 创建一行 Row row2 = sheet.createRow( 1 ); Cell cell21 = row2.createCell( 0 ); Cell cell22 = row2.createCell( 1 ); Cell cell23 = row2.createCell( 2 ); cell21.setCellValue( "75" ); cell22.setCellValue( "70" ); cell23.setCellValue( "66" ); // 5、生成工作簿 FileOutputStream fileOutputStream = null ; try { fileOutputStream = new FileOutputStream(WRITER_PATH + "考核成绩表07.xlsx" ); workbook.write(fileOutputStream); System.out.println( "工作表生成成功!" ); } catch (Exception e) { e.printStackTrace(); } finally { try { workbook.close(); assert fileOutputStream != null ; fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 写入大数据量Excel-03版本 */ @Test public void excelTargetBigData03() { long beginTime = System.currentTimeMillis(); // 1、创建工作簿 Workbook workbook = new HSSFWorkbook(); // 2、创建工作表 Sheet sheet = workbook.createSheet( "考核成绩表" ); for ( int rowNum = 0 ; rowNum < 6553 ; rowNum++) { Row row = sheet.createRow(rowNum); for ( int coluNum = 0 ; coluNum < 10 ; coluNum++) { Cell cell = row.createCell(coluNum); cell.setCellValue(coluNum); } } // 5、生成工作簿 FileOutputStream fileOutputStream = null ; try { fileOutputStream = new FileOutputStream(WRITER_PATH + "考核成绩表大数据写入03.xls" ); workbook.write(fileOutputStream); System.out.println( "工作表生成成功!" ); } catch (Exception e) { e.printStackTrace(); } finally { try { workbook.close(); assert fileOutputStream != null ; fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println( "耗时:" + (( double ) (System.currentTimeMillis()) - beginTime) / 1000 + "s" ); } /** * 写入大数据量Excel-07-普通版 * 写入10w条数据:9.149s */ @Test public void excelTargetBigData07() { long beginTime = System.currentTimeMillis(); // 1、创建工作簿 Workbook workbook = new XSSFWorkbook(); // 2、创建工作表 Sheet sheet = workbook.createSheet( "考核成绩表" ); for ( int rowNum = 0 ; rowNum < 100000 ; rowNum++) { Row row = sheet.createRow(rowNum); for ( int coluNum = 0 ; coluNum < 10 ; coluNum++) { Cell cell = row.createCell(coluNum); cell.setCellValue(coluNum); } } // 5、生成工作簿 FileOutputStream fileOutputStream = null ; try { fileOutputStream = new FileOutputStream(WRITER_PATH + "考核成绩表大数据写入07.xlsx" ); workbook.write(fileOutputStream); System.out.println( "工作表生成成功!" ); } catch (Exception e) { e.printStackTrace(); } finally { try { workbook.close(); assert fileOutputStream != null ; fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println( "耗时:" + (( double ) (System.currentTimeMillis()) - beginTime) / 1000 + "s" ); } /** * 写入大数据量Excel-07-缓存版本 * 写入10w条数据:2.161s */ @Test public void excelTargetBigDataCache07() { long beginTime = System.currentTimeMillis(); // 1、创建工作簿 Workbook workbook = new SXSSFWorkbook(); // 2、创建工作表 Sheet sheet = workbook.createSheet( "考核成绩表" ); for ( int rowNum = 0 ; rowNum < 100000 ; rowNum++) { Row row = sheet.createRow(rowNum); for ( int coluNum = 0 ; coluNum < 10 ; coluNum++) { Cell cell = row.createCell(coluNum); cell.setCellValue(coluNum); } } // 5、生成工作簿 FileOutputStream fileOutputStream = null ; try { fileOutputStream = new FileOutputStream(WRITER_PATH + "考核成绩表大数据写入Cache07.xls" ); workbook.write(fileOutputStream); System.out.println( "工作表生成成功!" ); } catch (Exception e) { e.printStackTrace(); } finally { try { workbook.close(); assert fileOutputStream != null ; fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println( "耗时:" + (( double ) (System.currentTimeMillis()) - beginTime) / 1000 + "s" ); } /** * 读取文件并判断列的类型 * * @throws Exception 异常 */ @Test public void testCellType() throws Exception { //获取文件流 FileInputStream fis = new FileInputStream(WRITER_PATH + "会员消费商品明细表.xls" ); //创建一个工作簿。使用 excel能操作的这边他都可以操作 Workbook workbook = new HSSFWorkbook(fis); Sheet sheet = workbook.getSheetAt( 0 ); //获取标题内容 Row rowTitle = sheet.getRow( 0 ); if (rowTitle != null ) { //得到一行有多少列有数据 int cellCount = rowTitle.getPhysicalNumberOfCells(); for ( int cellNum = 0 ; cellNum < cellCount; cellNum++) { Cell cell = rowTitle.getCell(cellNum); if (cell != null ) { String cellValue = cell.getStringCellValue(); System.out.print(cellValue + "|" ); } } System.out.println(); } // 遍历所有行 int rowCount = sheet.getPhysicalNumberOfRows(); for ( int rowNum = 1 ; rowNum < rowCount; rowNum++) { Row rowData = sheet.getRow(rowNum); // 遍历一行中的所有列 if (rowData != null ) { assert rowTitle != null ; int cellCount = rowTitle.getPhysicalNumberOfCells(); for ( int cellNum = 0 ; cellNum < cellCount; cellNum++) { System.out.print( "[" + (rowNum + 1 ) + "-" + (cellNum + 1 ) + "]" ); Cell cell = rowData.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( "--【日期】" ); LocalDateTime localDateTimeCellValue = cell.getLocalDateTimeCellValue(); cellValue = localDateTimeCellValue.format(DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" )); } else { //不是日期格式,防止数字过长! System.out.print( "--【转换为字符串输出】" ); cell.setCellType(CellType.STRING); cellValue = cell.toString(); } break ; //错误 case ERROR: System.out.print( "【 数据类型错误】" ); break ; default : break ; } System.out.println(cellValue); } } } } //关闭流 fis.close(); } public static int f(String s1, String s2) { if (s1.length() == 0 || s2.length() == 0 ) { return 0 ; } if (s1.charAt( 0 ) == s2.charAt( 0 )) { return f(s1.substring( 1 ), s2.substring( 1 )) + 1 ; } else { return Math.max(f(s1.substring( 1 ), s2), f(s1, s2.substring( 1 ))); } } } |
三、EasyExcel(推荐)
官方文档地址:https://easyexcel.opensource.alibaba.com/docs/current/
自己跟着文档练习即可。
导入Maven坐标:
1 2 3 4 5 | <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.1</version> </dependency> |
四、POI操作:使用自定义注解补充信息(Excel导出)
创建Maven项目,依赖如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.23</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.2</version> </dependency> </dependencies> |
定义注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /** * @author zhixi */ @Retention (RetentionPolicy.RUNTIME) @Target ({ElementType.FIELD, ElementType.TYPE}) @Documented public @interface Excel { /** * 字段别名 * * @return 字段名称 */ String name() default "" ; /** * 字典值 * * @return 字典值,用逗号分割,比如:"0=女,1=男" */ String dictValue() default "" ; } |
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | public class User { @Excel (name = "用户ID" ) private String id; @Excel (name = "姓名" ) private String name; @Excel (name = "年龄" ) private int age; /** * 性别 0-女 1-男 */ @Excel (name = "性别" , dictValue = "0=女,1=男" ) private int gender; public String getId() { return id; } public void setId(String id) { this .id = id; } public String getName() { return name; } public void setName(String name) { this .name = name; } public int getAge() { return age; } public void setAge( int age) { this .age = age; } public int getGender() { return gender; } public void setGender( int gender) { this .gender = gender; } @Override public String toString() { return "User{" + "name='" + name + '\ '' + ", age=" + age + ", gender=" + gender + '}' ; } public User(String name, int age, int gender) { this .name = name; this .age = age; this .gender = gender; } public User(String id, String name, int age, int gender) { this .id = id; this .name = name; this .age = age; this .gender = gender; } } |
导出Excel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | package com.zhixi; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.util.StringUtils; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; public class ExcelExporter { public static void export(List<User> users, OutputStream outputStream) { try (Workbook workbook = new XSSFWorkbook()) { // 创建一个工作表 Sheet sheet = workbook.createSheet(); // 获取所有的属性 Field[] declaredFields = User. class .getDeclaredFields(); // 获取所有包含Excel注解的字段 List<Field> filteredFields = Stream.of(declaredFields) .filter(f -> f.isAnnotationPresent(Excel. class )) .collect(Collectors.toList()); // 写入标题行 writeTitleRow(workbook,sheet, filteredFields); // 写入数据行 for ( int i = 0 ; i < users.size(); i++) { User user = users.get(i); // 数据从第二行开始写入(一行是标题) Row userRow = sheet.createRow(i + 1 ); writeDataRow(userRow, user, filteredFields); } // 写入文件 workbook.write(outputStream); } catch (IOException | IllegalAccessException e) { throw new RuntimeException(e); } } private static void writeTitleRow(Workbook workbook,Sheet sheet, List<Field> filteredFields) { // 创建新行 Row row = sheet.createRow( 0 ); // 设置表头加粗 CellStyle cellStyle = workbook.createCellStyle(); Font font = workbook.createFont(); font.setBold( true ); cellStyle.setFont(font); for ( int i = 0 ; i < filteredFields.size(); i++) { // 在所在行内创建新单元格 Cell cell = row.createCell(i); Excel excel = filteredFields.get(i).getAnnotation(Excel. class ); cell.setCellValue(excel.name()); cell.setCellStyle(cellStyle); } } private static void writeDataRow(Row userRow, User user, List<Field> filteredFields) throws IllegalAccessException { // 构造字段的值字典(处理dictValue这个属性) Map<Field, Map<String, String>> fieldDict = buildFieldDict(filteredFields); for ( int i = 0 ; i < filteredFields.size(); i++) { // 在行上面创建列,i表示列的下标 Cell cell = userRow.createCell(i); Field field = filteredFields.get(i); field.setAccessible( true ); // dictValue这个属性不为空,就从map字典中取出对应的值 Excel annotation = field.getAnnotation(Excel. class ); if (StringUtils.hasLength(annotation.dictValue())) { String key = String.valueOf(field.get(user)); cell.setCellValue(fieldDict.get(field).get(key)); } else { cell.setCellValue(String.valueOf(field.get(user))); } field.setAccessible( false ); } } private static Map<Field, Map<String, String>> buildFieldDict(List<Field> filteredFields) { Map<Field, Map<String, String>> fieldDict = new HashMap<>(); for (Field field : filteredFields) { Excel annotation = field.getAnnotation(Excel. class ); if (StringUtils.hasLength(annotation.dictValue())) { String d = annotation.dictValue(); String[] kvs = d.split( "," ); Map<String, String> map = new HashMap<>(); for (String kv : kvs) { String[] split = kv.split( "=" ); map.put(split[ 0 ], split[ 1 ]); } fieldDict.put(field, map); } } return fieldDict; } public static void main(String[] args) throws IOException { List<User> users = new ArrayList<>(); users.add( new User( "1" , "张三" , 20 , 1 )); users.add( new User( "2" , "李四" , 30 , 0 )); users.add( new User( "3" , "王五" , 40 , 1 )); System.out.println(Paths.get( "." ).getFileName()); OutputStream outputStream = Files.newOutputStream(Paths.get( "users.xlsx" )); ExcelExporter.export(users, outputStream); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
2020-12-28 8、Mybatis分页
2020-12-28 7、mybatis日志工厂
2020-12-28 6、ResultMap结果集映射-解决实体类属性名和数据库字段名不一致的问题
2020-12-28 5、mybatis生命周期和作用域