Java开发中如何用POI优雅的导出Excel文件
前言
故事是这样开始的: 公司给排了几天的工期,让完成2个功能模块的开发。其中有一个场景是这样的,从Excel导入数据,要求数据不能重复。用户可以下载导入失败的Excel文件。
这样就有2种实现
将失败数据存储数据库,需要下载时生成Excel下载即可
将失败数据生成Excel文件存储文件服务器,然后返回下载链接。
老大要求按方案二进行。好吧,导出Excel是再常见不过的功能了,然而总是觉得以前写的不够优雅,所以决定进行简单的封装,以适应简单场景的Excel导出。
实现
Excel导出的数据源一般是数据库中查询的数据。在Java开发这种面向对象的设计中,数据一般都是以对象为载体,填充在集合中的。所以入参之一便设计为Collection集合类。
导出的Excel一般需要表头说明每列数据的含义,这里计划用注解+反射来进行定义。Excel无论是通过 HttpServletResponse
还是 File
存储,本质上都是IO流操作。
具体实现如下:
pom.xml
1 2 3 4 5 <dependency > <groupId > org.apache.poi</groupId > <artifactId > poi-ooxml</artifactId > <version > 4.1.2</version > </dependency >
注解定义
该注解添加在字段上,标识哪些字段需要导出到Excel,并且添加表头说明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExcelHeader { String value () default "" ; int columnIndex () default 0 ; }
实体类
以下实体类中根据注解可知,age字段不需要导出到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 @Data @NoArgsConstructor @AllArgsConstructor public class User { @ExcelHeader(value = "账号") private String username; @ExcelHeader(value = "密码") private String password; @ExcelHeader(value = "生日") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime birthday; private Integer age; public User (String username, String password, LocalDateTime birthday) { this .username = username; this .password = password; this .birthday = birthday; } }
单元测试
注释已经足够清晰,这里不再过多复述。
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 @Slf4j class IdcPoiApplicationTests { @Test void contextLoads () throws NoSuchFieldException, IllegalAccessException, IOException { User user1 = new User("admin" , "123456" , LocalDateTime.now()); User user2 = new User("test" , "123456" , LocalDateTime.now()); List<User> users = Arrays.asList(user1, user2); String basePath = "/Users/cuishiying/Documents/spring-cloud-learning/idc-poi/oss/" ; String fileName = new SimpleDateFormat("yyyyMMddHHmmss" ).format(new Date()).toString() +".xls" ; HSSFWorkbook workbook = exportExcel(users, User.class); workbook.setSheetName(0 ,"sheetName" ); workbook.write(new File(basePath + File.separator + fileName)); } public <T> HSSFWorkbook exportExcel (List<T> data, Class<T> clz) throws NoSuchFieldException, IllegalAccessException { Field[] fields = clz.getDeclaredFields(); List<String> headers = new LinkedList<>(); List<String> variables = new LinkedList<>(); HSSFWorkbook workbook=new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet(); Row rowHeader = sheet.createRow(0 ); for (int h = 0 ; h < fields.length; h++) { Field field = fields[h]; if (field.isAnnotationPresent(ExcelHeader.class)) { if (field.isAnnotationPresent(ExcelHeader.class)) { ExcelHeader annotation = field.getAnnotation(ExcelHeader.class); headers.add(annotation.value()); rowHeader.createCell(h).setCellValue(annotation.value()); } variables.add(field.getName()); } } for (int i = 0 ; i < data.size() ; i++) { HSSFRow row = sheet.createRow(i + 1 ); T t = data.get(i); Class<?> aClass = t.getClass(); for (int j = 0 ; j < variables.size(); j++) { Field declaredField = aClass.getDeclaredField(variables.get(j)); declaredField.setAccessible(true ); String key = declaredField.getName(); Object value = declaredField.get(t); row.createCell(j).setCellValue(value.toString()); } } log.info("Excel文件创建成功" ); return workbook; } }
Web导出
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 @Slf4j @RestController public class ExcelController { @GetMapping("export") public void export (HttpServletResponse response) throws Exception { User user1 = new User("admin" , "123456" , LocalDateTime.now()); User user2 = new User("test" , "123456" , LocalDateTime.now()); List<User> users = Arrays.asList(user1, user2); HSSFWorkbook workbook = ExcelUtils.exportExcel(users, User.class); workbook.setSheetName(0 ,"sheetName" ); String downloadName = new SimpleDateFormat("yyyyMMddHHmmss" ).format(new Date()).toString() +".xls" ; response.setContentType("application/vnd.ms-excel;charset=utf-8" ); response.setHeader("Content-Disposition" , "attachment;filename=" + URLEncoder.encode(downloadName, StandardCharsets.UTF_8)); OutputStream outputStream = response.getOutputStream(); workbook.write(outputStream); outputStream.close(); } }
总结
上边的案例只是表达基本设计思路。大家项目中使用的话还请斟酌。本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】支持一下。