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
<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的表单数据
<!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
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项目,导入依赖
<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、案例测试
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坐标:
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.1</version> </dependency>
四、POI操作:使用自定义注解补充信息(Excel导出)
创建Maven项目,依赖如下
<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>
定义注解
/** * @author zhixi */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.TYPE}) @Documented public @interface Excel { /** * 字段别名 * * @return 字段名称 */ String name() default ""; /** * 字典值 * * @return 字典值,用逗号分割,比如:"0=女,1=男" */ String dictValue() default ""; }
实体类
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
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); } }