框架踩坑之excel模板导出

747 阅读3分钟

记录在完成项目中的excel模板导出时,踩到的坑及最后的解决方案

项目框架是 JEECG-BOOT(2.4.6)

踩坑描述

需要在excel导出的表格的表尾处加上几个签字人。

用过这个框架的应该会知道,导出excel用到的是autopoi库,版本是1.3.5,就是这个版本让模板导出变得如此的艰难。

原因分析

一开始我把模板文件放在了resources资源文件夹下面,然后在官网上找了个栗子,简单测试一下,不行,提示文件没有找到。于是我这个菜鸡开始用这个错误信息疯狂在网上找解决方案,测试了很多其他的代码,除了把模板代码放在固定的盘符,用绝对路径访问,其他的方式都不行。

我开始思考,这个库是不是不能访问resources下的文件啊。于是找到了autopoi实例化模板文件的代码

image-20210915183454366.png

果然,这里把获取模板文件输入流的方法换成了new FileInputStream(url);。明明上个版本还是通过ClassPathResource获取资源文件的,当前版本只能获取本地文件了。

解决方案

既然当前库的版本不符合要求了,要么降低版本,要么新建项目。因为整个项目已经做了一半了,现在贸然修改版本,可能会对之前已完成的功能造成影响。所以我选择了新建一个单独的项目来做模板导出,既然是单独的项目,那么技术选型上我选择了easypoi-4.1。

因为担心也存在找不到资源文件这种类似的问题,所以我先找到了库里获取文件这块的代码。

image-20210916091241266.png

很显然,easypoi不仅支持资源文件,还支持网络路径的文件。参考官方文档给出的示例,完成了这个单独的服务。

具体逻辑是,这个服务是以post的方式接收一个传入的字符串,然后通过JSONObject提供的方法将其转化为我需要的类型(为什么是这样做,请往下看),封装模板需要的参数,进行模板导出。附上代码

    @PostMapping("/excel")
    public void exportExcel(@RequestParam("dataStr") String dataStr, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 将表单提交的值转化为需要的实体类
        ExportExcelModel exportExcelModel = JSONObject.parseObject(dataStr, ExportExcelModel.class);
        // 渲染需要的参数
        HashMap<String, Object> renderMap = new HashMap<>();
        // 模板地址
        TemplateExportParams params = new TemplateExportParams(template + "/" + exportExcelModel.getTemplateName());
        renderMap.put(TemplateExcelConstants.PARAMS, params);
        // 导出文件名
        renderMap.put(TemplateExcelConstants.FILE_NAME, exportExcelModel.getFilename());
        // 导出模板里需要的参数
        renderMap.put(TemplateExcelConstants.MAP_DATA, exportExcelModel.getMap());
        // 开始渲染
        PoiBaseView.render(renderMap, request, response, TemplateExcelConstants.EASYPOI_TEMPLATE_EXCEL_VIEW);
    }

到这里,仍然还是没有完全解决这个需求。因为我这个是一个单独部署的服务,所以导出模板时需要的数据也是通过接口的方式传入的,数据量大小未知,所以显然是不能以get方式来访问的。但是我下文件是通过window.open()的方式来执行的,那么window.open怎么发出一个post请求呢?

window.open发出post请求

万能的百度总是可以找到解决方案的。window.open发出一个post请求的本质是打开一个空白窗口,在窗口里创建一个表单,执行表单的提交,然后销毁窗口。 附上vue版实现的代码

    async handleExportStoWarehousingList(record) {
      this.loading = true
      const res = await getAction(this.url.exportDataUrl, { id: record.id })
      if (res.success) {
        let params = {
          templateName: 'xx.xls',
          filename: record.xx,
          map:res.result
        }
        openNewWindowUsePost(JSON.stringify(params))
      }
      this.loading = false
    },
/**
 *
 * @param url 以post方式打开一个新的请求地址
 * @param dataStr 请求参数
 */
export function openNewWindowUsePost(dataStr, url = window._CONFIG['exportBaseUrl'] + 'excel') {
  let newWindow = window.open('about:blank', '_blank')
  newWindow.document.body.innerHTML = `
  <form action="${url}" method="post">
  <input type="text" name="dataStr" value='${dataStr}' style="display: none">
  </form>
  `
  newWindow.document.forms[0].submit()
  setTimeout(() => {
    newWindow.close()
  }, 50)
}

总结

其实这个踩坑的根源是原本框架不支持resources下资源文件的访问(也可能是我没发现其中的奥秘),所以我才另辟蹊径新建一个单独的服务来执行模板导出。当然这个方案也有它的可取之处,因为easypoi支持网络文件,所以我现在可以把模板放在可访问的任何地方,并且是单独的服务,还可以对其他的项目提供接口。同时也学习到了window.open发出post请求的小技巧。

领导来了,接着撸代码了。。。