业务场景:由于公司发展需要,现有大量的导入导出工作,一直都是用的apache-poi,现在部门领导要求使用EasyExcel(版本为 2.1.3),听说性能更好!EasyExcel重写了poi对Excel文件的解析逻辑,基本不会出现大文件解析导致的内存溢出。

于是我就上手了,但是!!!
因为公司强烈使用 LocalDateTime (注意EasyExcel是支持Date类型的,可以直接导入导出!)
所以我在做导入导出的时候就遇到问题了,报错如下:
Can not find 'Converter' support class LocalDateTime.

可是百度了一大堆,Google了一大堆都没有解决方案。。。如果不懂EasyExcel建议先去官网了解一下,很详细,好啦我就默认你已经了解过EasyExcel了,不多逼逼,上代码!

新建User类,然后在类上添加EasyExcel的注解

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
	@ExcelProperty(value = "姓名", index = 0)
	private String name;
	@ExcelProperty(value = "年龄", index = 1)
	private Integer age;
	@ExcelProperty(value = "性别", index = 2)
	private Integer sex;
	@ExcelProperty(value = "创建时间", index = 3)
	private LocalDateTime createTime;
}

新建Controller用于测试

@Slf4j
@RestController
public class ExcelController {

	@PostMapping("/importData")
	public void importData(@RequestParam("file") MultipartFile file) throws IOException {
		if (file == null) return;
		ArrayList<Object> list = new ArrayList<>();
		AnalysisEventListener listener = new AnalysisEventListener() {
			@Override
			public void invoke(Object data, AnalysisContext context) {
				list.add(data);
			}

			@Override
			public void doAfterAllAnalysed(AnalysisContext context) {
				log.info("导入数据完毕");
			}
		};
		try {
			EasyExcel.read(file.getInputStream(), User.class, listener).sheet(0).doRead();
		} catch (IOException e) {
			log.error("导入出错:{}", e.getMessage());
		}
		list.forEach(System.out::println);
	}

	@PostMapping("/exportData")
	public void exportData(HttpServletResponse response) {
		List<User> list = getList();
		try {
			response.setContentType("application/vnd.ms-excel; charset=utf-8");
			response.setCharacterEncoding("utf-8");
			String fileName = "三好学生表";
			response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8") + ".xlsx");
			EasyExcel.write(response.getOutputStream(), User.class).sheet("test").doWrite(list);
		} catch (Exception e) {
			log.error("下载报表异常:{}", e.getMessage());
			throw new RuntimeException("下载报表异常");
		}
	}

	private List<User> getList() {
		List<User> list = new ArrayList<>();
		LocalDateTime now = LocalDateTime.now();
		User xd = new User("熊大", 10, 1, now);
		User ne = new User("牛二", 20, 0, now);
		User zs = new User("张三", 30, 1, now);
		User ls = new User("李四", 40, 1, now);
		User ww = new User("王五", 50, 0, now);
		list.add(xd);
		list.add(ne);
		list.add(zs);
		list.add(ls);
		list.add(ww);
		return list;
	}
}

访问exportData接口即会报如上的错,那么怎么解决呢?首先我可以告诉你解决方案!就是自定义转换器,因为我们要转换LocalDateTime,所以编写如下转换器!注意要实现的是com.alibaba.excel.converters.Converter接口,别引用错包了!

public class LocalDateTimeConverter implements Converter<LocalDateTime> {

	@Override
	public Class<LocalDateTime> supportJavaTypeKey() {
		return LocalDateTime.class;
	}

	@Override
	public CellDataTypeEnum supportExcelTypeKey() {
		return CellDataTypeEnum.STRING;
	}

	@Override
	public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
	                                       GlobalConfiguration globalConfiguration) {
		return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
	}

	@Override
	public CellData<String> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
	                                           GlobalConfiguration globalConfiguration) {
		return new CellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
	}

}

划重点!LocalDateTime的converter编写完了,怎么才能让它生效呢?有三种解决方案!

  1. 找到需要转换的字段,在@ExcelProperty上添加converter属性

  2. ExcelWriterBuilders是支持对单次的操作添加converter的,那样就不需要为需要转换的字段单独添加converter了

  3. 定义全局Converter加载器以及Converter使用总结

以上三种方式只需使用一种!修改代码完毕之后再重启就可以了,不管是导入还是导出都没问题!时间已经可以正常写入Excel啦!赶快试试吧!

思考一下上述的前两种方式是否有弊端,点击第三种方案带你追踪一下源码并给出具体的解决方案!

加油!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐