记录在完成项目中的excel模板导出时,踩到的坑及最后的解决方案
项目框架是 JEECG-BOOT(2.4.6)
踩坑描述
需要在excel导出的表格的表尾处加上几个签字人。
用过这个框架的应该会知道,导出excel用到的是autopoi库,版本是1.3.5,就是这个版本让模板导出变得如此的艰难。
原因分析
一开始我把模板文件放在了resources
资源文件夹下面,然后在官网上找了个栗子,简单测试一下,不行,提示文件没有找到。于是我这个菜鸡开始用这个错误信息疯狂在网上找解决方案,测试了很多其他的代码,除了把模板代码放在固定的盘符,用绝对路径访问,其他的方式都不行。
我开始思考,这个库是不是不能访问resources
下的文件啊。于是找到了autopoi实例化模板文件的代码
果然,这里把获取模板文件输入流的方法换成了new FileInputStream(url);
。明明上个版本还是通过ClassPathResource
获取资源文件的,当前版本只能获取本地文件了。
解决方案
既然当前库的版本不符合要求了,要么降低版本,要么新建项目。因为整个项目已经做了一半了,现在贸然修改版本,可能会对之前已完成的功能造成影响。所以我选择了新建一个单独的项目来做模板导出,既然是单独的项目,那么技术选型上我选择了easypoi-4.1。
因为担心也存在找不到资源文件这种类似的问题,所以我先找到了库里获取文件这块的代码。
很显然,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请求的小技巧。
领导来了,接着撸代码了。。。