基于easypoi实现自定义模板导出excel

x33g5p2x  于2021-12-19 转载在 其他  
字(4.6k)|赞(0)|评价(0)|浏览(697)

项目中需要做一个统计报表功能,实现各种Excel报表数据导出。要求表头能够动态配置,表数据通过存储过程实现,也要求能够动态配置。

技术选型:
由于之前在项目中使用过easypoi,相对于原生apache poi,能够用很少的代码写出Excel导入、导出功能,且API清晰好理解。因此优先选择了使用easypoi,验证功能需求能否实现。easypoi是基于apache poi开发,在此基础上进行了封装和扩展,特别复杂的功能就需要使用基础poi来开发了。

开发指南:https://opensource.afterturn.cn/doc/easypoi.html

实现思路:
由于配置的报表多是复杂多级表头,而easypoi对于动态表头生成只支持两级,简单来说就是表头最多两行,所以这种方式就只能放弃。改选用配置动态模板的方式,先做好模板,然后配置到数据表里。

实现步骤:
Maven pom中引入jar包

  1. <dependency>
  2. <groupId>cn.afterturn</groupId>
  3. <artifactId>easypoi-base</artifactId>
  4. <version>3.0.1</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>cn.afterturn</groupId>
  8. <artifactId>easypoi-web</artifactId>
  9. <version>3.0.1</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>cn.afterturn</groupId>
  13. <artifactId>easypoi-annotation</artifactId>
  14. <version>3.0.1</version>
  15. </dependency>

ReportController类: 如下代码仅显示主要步骤:

  1. @RequestMapping("/exportExcel.html")
  2. @ResponseBody
  3. public void exportExcel(HttpServletResponse response, HttpSession session) {
  4. // 获取报表配置 ReportResultVo主要存储了 标题行数、模板路径位置、导出文件名称等
  5. ReportResultVo config = reportService.getReportConfig(id);
  6. TemplateExportParams params = new TemplateExportParams();
  7. // 标题开始行
  8. params.setHeadingStartRow(0);
  9. // 标题行数
  10. params.setHeadingRows(config.getHeadRowNum());
  11. // 设置sheetName,若不设置该参数,则使用得原本得sheet名称
  12. params.setSheetName("数据统计");
  13. // 获取报表内容
  14. // 因为表数据是根据存储过程来实现的,不同的报表有不同的配置,
  15. // 所以使用Map<String,Object>格式来接收
  16. List<Map<String, Object>> reportBodyList = reportService.getReportBodyData(...);
  17. Map<String, Object> data = new HashMap<String, Object>();
  18. data.put("list", reportBodyList);
  19. // 获取模板文件路径
  20. // 这里有个很坑的地方,就是easypoi的API只能接收文件路径,无法读取文件流
  21. String filePath = 服务器上的某个路径或者项目中的某个路径
  22. // 设置模板路径
  23. params.setTemplateUrl(filePath);
  24. // 获取workbook
  25. Workbook workbook = ExcelExportUtil.exportExcel(params, data);
  26. // exportFileName代表导出的文件名称
  27. ReportUtils.export(response, workbook, exportFileName);

ReportUtils类:

  1. // Excel 导出 通过浏览器下载的形式
  2. public static void export(HttpServletResponse response, Workbook workbook, String fileName) throws IOException {
  3. response.setHeader("Content-Disposition",
  4. "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "iso8859-1"));
  5. response.setContentType("application/vnd.ms-excel;charset=UTF-8");
  6. response.setHeader("Pragma", "no-cache");
  7. response.setHeader("Cache-Control", "no-cache");
  8. response.setDateHeader("Expires", 0);
  9. BufferedOutputStream bufferedOutPut = new BufferedOutputStream(response.getOutputStream());
  10. workbook.write(bufferedOutPut);
  11. bufferedOutPut.flush();
  12. bufferedOutPut.close();
  13. }

模板样式:

模板以{{$fe:list 开头,以}}结尾,代表变遍历数据的意思,每个字段前面的t.前缀是easypoi指定的默认值。

获取的报表内容字段名称要与模板里的字段一一对应

  1. List<Map<String, Object>> reportBodyList = new ArrayList<>();
  2. Map<String,Object> values = new HashMap<StringObject>();
  3. values.put(c1,总计);
  4. values.put(c2,10);
  5. values.put(c3,5);
  6. values.put(c4,8);
  7. values.put(c5,5);
  8. values.put(c6,8);
  9. values.put(c7,6);
  10. values.put(c8,3);
  11. reportBodyList.add(values);

导出的Excel结果如下:

到目前为止,已经可以实现需求了,但是实现的不够好,尤其是上面提到的easypoi无法读取文件流,只能从本地路径上获取文件模板,极大的限制了程序的灵活性。而生产环境中的项目大多都会使用文件存储服务器,比如fastdfs,而不是把模板上传到web服务器上的某个路径下。

还有别的解决办法吗?实在无法实现需求的话就只能使用apache poi了,但是这种方式改动太大,虽然可以灵活定制excel样式,但是实现要复杂的多。思考良久后,决定使用临时文件的方式解决这个问题。

实现思路:
从fastdfs中获取文件流后,写到本地临时目录,然后让easypoi从本地临时目录里读取模板文件,最后再删除临时文件。

关键代码如下:

  1. @RequestMapping("/exportExcel.html")
  2. @ResponseBody
  3. public void exportExcel(HttpServletResponse response, HttpSession session) {
  4. ......
  5. try{
  6. // 从fastDfs上获取文件流 (fileStorage.readFile自己封装的API)
  7. InputStream inputStream = fileStorage.readFile(filepath);
  8. // 模板临时目录
  9. String rootPath = session.getServletContext().getRealPath(“template_temp/”);
  10. // 临时文件路径名
  11. String filePath = rootPath + "_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + filename;
  12. tempFile = new File(filePath);
  13. // 保存到临时文件
  14. ReportUtils.saveTempFile(inputStream, tempFile);
  15. // 设置模板路径
  16. params.setTemplateUrl(filePath);
  17. // 获取workbook
  18. Workbook workbook = ExcelExportUtil.exportExcel(params, data);
  19. // exportFileName代表导出的文件名称
  20. ReportUtils.export(response, workbook, exportFileName);
  21. } catch (Exception e) {
  22. throw new GeneralException(ErrorCode.REPORT_EXPORT_EXCEPTION);
  23. } finally {
  24. // 删除临时文件
  25. if (tempFile.exists()) {
  26. tempFile.delete();
  27. }
  28. }
  29. }

ReportUtils类:

  1. // 保存到临时目录
  2. public static void saveTempFile(InputStream inputStream, File tempFile) throws IOException {
  3. if(!tempFile.getParentFile().exists()){ //如果文件的目录不存在
  4. tempFile.getParentFile().mkdirs(); //创建目录
  5. }
  6. OutputStream os = new FileOutputStream(tempFile);
  7. byte[] b = new byte[2048];
  8. int length;
  9. while ((length = inputStream.read(b)) > 0) {
  10. os.write(b, 0, length);
  11. }
  12. os.flush();
  13. os.close();
  14. inputStream.close();
  15. }

至此,代码实现较好的满足了动态配置的需要,如果大家有更好的方法,欢迎提出!

------------本文结束感谢您的阅读------------

相关文章