本着不重复造轮子的开发思想,我们对Excel表格的操作都通过easyExcel进行实现

使用开源轮子实现Excel下载、导入、导出的功能。

一、项目集成

1、导包

  1. <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>easyexcel</artifactId>
  5. <version>2.2.7</version>
  6. </dependency>

1、实现已有Excel模板下载

很多系统有数据批量导入的场景,因为在页面上批量加数据时间成本太大了,但是一般导入的时候得按照一定的格式改,所以一般好的产品会先让用户下载一个带有格式的文档,然后按照格式写好以后上传导入

根据自己的项目创建一个Excel模板,放到项目配置文件下
image.png

目录结构如下
image.png

二、功能实现

1、下载

文件模板已经准备好了,实现下载模板的功能

下载功能代码也很简单,主要分为加载资源->读取资源->写入响应流

实现如下:

  1. /**
  2. * 下载模板
  3. */
  4. @GetMapping("/downloadTemplate")
  5. public void downloadTemplate(HttpServletResponse response) throws Exception {
  6. //获取模板文件
  7. String fileName = "template_.xlsx";
  8. String templatePath = "excelTemplate/" + fileName;
  9. ClassPathResource classPathResource = new ClassPathResource(templatePath);
  10. InputStream inputStream = classPathResource.getInputStream();
  11. ServletOutputStream servletOutputStream = null;
  12. try {
  13. response.setContentType("application/force-download");
  14. response.setHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes(), StandardCharsets.UTF_8));
  15. servletOutputStream = response.getOutputStream();
  16. IoUtil.copy(inputStream, servletOutputStream);
  17. response.flushBuffer();
  18. } catch (Exception e) {
  19. log.error("下载失败,文件模板{}不存在", fileName);
  20. } finally {
  21. if (servletOutputStream != null) {
  22. servletOutputStream.close();
  23. }
  24. inputStream.close();
  25. }
  26. }

注意:

XSSF操作的是Excel2007以上的版本,对应文件的后缀名是xlsx
Workbook workbook = new XSSFWorkbook(inputStream)

HSSF操作的是Excel2003以前的版本,对应的文件后缀名是xls
Workbook workbook = new HSSFWorkbook(inputStream)

2、导入

将数据导出到文档这种场景可以说是最常见的了,那么怎么使用easyExcel快速实现呢,我们同样还是以上面的模板为例

定义模型映射对象

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. public class UserExcelModel implements Serializable {
  5. private static final long serialVersionUID = 2723059827514336242L;
  6. @ExcelProperty(value = "用户名", index = 0)
  7. private String name;
  8. @ExcelProperty(value = "年龄", index = 1)
  9. private Integer age;
  10. @ExcelProperty(value = "性别", index = 2)
  11. private String sex;
  12. @ExcelProperty(value = "手机号", index = 3)
  13. private String mobile;
  14. }

注意模型映射对象一定要有默认的无参构造函数
导入数据的主要流程如下:
定义列标题->创建sheet->自定义字体和风格->构造数据->写入数据->写入到浏览器响应流

代码实现如下:

  1. /**
  2. * 读取数据
  3. *
  4. * @param file 文件名称
  5. * @return list
  6. */
  7. @PostMapping("/readExcel")
  8. public List<UserExcelModel> readExcel(@RequestParam("file") MultipartFile file) {
  9. List<UserExcelModel> list = new ArrayList<>();
  10. try {
  11. list = EasyExcel.read(file.getInputStream(), UserExcelModel.class, new ModelExcelListener()).sheet().doReadSync();
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. }
  15. return list;
  16. }

读取监听

  1. @Slf4j
  2. public class ModelExcelListener extends AnalysisEventListener {
  3. private final List<Object> dataList = new ArrayList<>();
  4. /**
  5. * 通过 AnalysisContext 对象可以获取到当前sheet,当前行等数据信息
  6. *
  7. * @param data 数据
  8. * @param context 表格对象
  9. */
  10. @Override
  11. public void invoke(Object data, AnalysisContext context) {
  12. //数据存储到list,供批量处理,或后续自己业务逻辑处理。
  13. log.info("读取到数据{}", data);
  14. dataList.add(data);
  15. //根据业务自行处理,可以写入数据库等等
  16. }
  17. /**
  18. * 数据解析完成回调
  19. *
  20. * @param context 表格对象
  21. */
  22. @Override
  23. public void doAfterAllAnalysed(AnalysisContext context) {
  24. log.info("所有数据解析完成");
  25. }
  26. }

3、导出

  1. /**
  2. * 导出数据,尽量导出成低版本
  3. */
  4. @GetMapping("/exportData")
  5. public void exportData(HttpServletResponse response) throws Exception {
  6. //生成工作簿
  7. XSSFWorkbook workbook = new XSSFWorkbook();
  8. //定义列标题,尽量遵从实体类的index
  9. String[] columnNames = {"用户名", "年龄", "性别", "手机号"};
  10. //定义工作表
  11. Sheet sheet = workbook.createSheet();
  12. //定义标题样式
  13. Font titleFont = workbook.createFont();
  14. titleFont.setFontName("宋体");
  15. titleFont.setBold(true);
  16. titleFont.setColor(IndexedColors.BLACK.index);
  17. //设置单元格样式
  18. XSSFCellStyle titleStyle = workbook.createCellStyle();
  19. titleStyle.setAlignment(HorizontalAlignment.CENTER);
  20. titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
  21. titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
  22. titleStyle.setFillForegroundColor(IndexedColors.YELLOW.index);
  23. titleStyle.setFont(titleFont);
  24. //定义行
  25. Row titleRow = sheet.createRow(0);
  26. //设置列
  27. for (int i = 0; i < columnNames.length; i++) {
  28. Cell cell = titleRow.createCell(i);
  29. cell.setCellValue(columnNames[i]);
  30. cell.setCellStyle(titleStyle);
  31. }
  32. //模拟构造数据
  33. List<UserExcelModel> dataList = new ArrayList<>();
  34. dataList.add(new UserExcelModel("张三", 12, "男", "13867098765"));
  35. dataList.add(new UserExcelModel("张三1", 12, "男", "13867098765"));
  36. dataList.add(new UserExcelModel("张三2", 12, "男", "13867098765"));
  37. dataList.add(new UserExcelModel("张三3", 12, "男", "13867098765"));
  38. //创建数据行并写入值
  39. for (UserExcelModel userExcelModel : dataList) {
  40. int lastRowNum = sheet.getLastRowNum();
  41. Row dataRow = sheet.createRow(lastRowNum + 1);
  42. // 此处注意index的顺序一定要严格按照模板文件的格式
  43. dataRow.createCell(0).setCellValue(userExcelModel.getName());
  44. dataRow.createCell(1).setCellValue(userExcelModel.getAge());
  45. dataRow.createCell(2).setCellValue(userExcelModel.getSex());
  46. dataRow.createCell(3).setCellValue(userExcelModel.getMobile());
  47. }
  48. response.setContentType("application/vnd.ms-excel");
  49. response.setHeader("content-Disposition", "attachment;filename=" + URLEncoder.encode("easyexcel.xls", "utf-8"));
  50. response.setHeader("Access-Control-Expose-Headers", "content-Disposition");
  51. OutputStream outputStream = response.getOutputStream();
  52. workbook.write(outputStream);
  53. outputStream.flush();
  54. outputStream.close();
  55. }

附录:

github地址:https://github.com/alibaba/easyexcel