Java使用FreeMarker模板引擎导出Excel
导出Excel的方式有许多,例如,使用POI自定义单元格,EasyPOI封装的各种导出等等。但是对应一些复杂的Excel,使用FreeMarker模板引擎导出Excel可能更为方便。废话不多说了,上源码:
1,设计模板
将需要替换的数据,如果是单个属性值,使用${属性值},如果是集合list,使用 ${list.属性值},如下图:
2,转为XML
填好之后另存为 XML电子表格,如下图:
3,修改模板
将模板放到项目下或者固定路径下,这个根据项目的要求,如果需要对里面的数据判断赋值可以使用 ${属性值!“”},如下图:
4,编写代码
- 服务端代码
public void export(@RequestBody Map<String, Object> map, HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> rmap = null;
Map<String, Object> cmap = null;
Map<String, Object> data = new HashMap<>();
// 配置模板属性
Template t = null;
Configuration configuration = new Configuration(new Version("2.3.0"));
configuration.setDefaultEncoding("UTF-8");
String time = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
//导出的文件名
String fileName = "D:\\template\\bxd_" + time + ".xls";
Writer out = null;
try {
request.setCharacterEncoding("UTF-8");
configuration.setDirectoryForTemplateLoading(new File(request.getRealPath("/") + "/template/"));
//获取模板文件
t = configuration.getTemplate("bxd_template.xml");
// 查询数据
Xs_bxd xs_bxd = bxdService.getById(id);
if (xs_bxd != null) {
data.put("ccr", xs_bxd.getBusTraveller());
data.put("bm", xs_bxd.getDepartment());
data.put("zw", xs_bxd.getDuty());
data.put("cxsj", dateToTimeStrFormat(xs_bxd.getLeaveTime()));
data.put("fhsj", dateToTimeStrFormat(xs_bxd.getRevolveTime()));
data.put("dlr", xs_bxd.getDeputy());
data.put("ptry", xs_bxd.getEscort());
data.put("ccsy", xs_bxd.getBusinessMatters());
data.put("ccdd", xs_bxd.getPlaceBusiness());
data.put("yjjp", xs_bxd.getFlightTicket());
data.put("jtgj", xs_bxd.getVehicle());
data.put("yjlf", xs_bxd.getAdvanceTravel());
}
List<Xs_bxd_mx> xs_bxd_mxs = bxdMxService.getExportBxdList(id);
data.put("list", xs_bxd_mxs);
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), "utf-8"));
// 将填充数据填入模板文件并输出到目标文件
t.process(data, out);
WordUtil.download(response, new File(fileName));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
- WordUtil.download()方法的代码
public synchronized static void download(HttpServletResponse response, File file) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
response.setContentType("application/octet-stream");
// 下载文件能正常显示中文
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
bis = new BufferedInputStream(new FileInputStream(file));
bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[10240];
int bytesRead;
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
bis.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
//这里为测试代码,可以自行封装
String fileName = "D:\\test.docx";
File f = new File(fileName);
if (f.exists()) {
f.delete();
}
}
}
- 前端代码
这里前端使用了Vue和iView的组件,使用Ajax进行请求
// 导出当前的数据
export(e) {
let that = this;
that.$Modal.confirm({
title: "确认导出",
content: "确认导出该条数据吗?",
onOk: () => {
// 原生ajax
let xhr = new XMLHttpRequest();
//post方式请求后台的路径
xhr.open('post', '../bxd/export.do', true);
//导出的Excel是二进制数据类型,因此设置为blob
xhr.responseType = 'blob';
//请求头(key,value),请求头能够设置多个key-value对
xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8');
//返回成功,导出的Excel文件
xhr.onload = function () {
if (this.status == 200) {
let blob = this.response;
let a = document.createElement('a');
let url = window.URL.createObjectURL(blob);
a.href = url;
//设置文件名称
a.download = e.busTraveller + "报销单(" + that.formatDate(new Date(), 'yyyy-MM-dd') + ").xls";
a.click();
} else {
that.$Spin.hide();
}
}
//请求的参数,json格式,后台要用json格式接收
xhr.send(JSON.stringify({
"id": e.id
}));
}
})
},
遇到的问题:导出的Excel使用WPS可以打开,使用Office打开可能出现以下提示信息:
XML模板里面搜一下 ExpandedRowCount,修改模板这里的值为固定值或者修改为动态的值(${data?size + 10},这里+10不是一定的,多少都行),或者大于当前的行(列)数,