之前项目中使用freemarker和POI进行word以及excel的模板导出,在使用的过程中为了解决一些小问题,意外的接触了Beetl这款模板生成导出方案,解决了一些痛点,所以这次就进行总结分享下。
com.ibeetl
beetl
2.9.3
创建模板 doc或者xls,设置好样式以及在需要动态加载的地方填充好一些数据(替换变量的时候方便定位),然后另存为xml格式文件;用文本编辑器打开进行模板制作。
根据beetl的语法,在早先设置的数据处替换为变量,然后修改文件后缀名为btl,即制作好了模板。java项目的话,则放在templates目录下。
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
/**
* Created on 2018/10/16.
* Beetl生成doc文档工具类
*/
public class BeetlUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(BeetlUtils.class);
/** 模板路径 */
private static final String TEMPLATE_PATH = "templates/";
/** 模板组 */
private static GroupTemplate gt ;
/**
* 获取到模板
* @param templateName 模板名称
* @return beetle模板
* @throws Exception 异常
*/
public static Template getTemplate(String templateName)throws Exception{
if (gt==null) {
ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader(TEMPLATE_PATH);
Configuration cfg = Configuration.defaultConfiguration();
gt = new GroupTemplate(resourceLoader, cfg);
}
return gt.getTemplate(templateName+".btl");
}
public static String renderToString(Map data,String templateName) throws Exception{
Template template =getTemplate(templateName);
template.binding(data);
return template.render();
}
/**
* 下载doc文件
* @param dataMap 模板数据
* @param template 模板
* @throws Exception 异常
*/
public static void exportWord(String fileName, Map dataMap,Template template,HttpServletResponse response) throws Exception {
setFileDownloadHeader(response, fileName);
template.binding(dataMap);
template.renderTo(response.getOutputStream());
}
/**
* 下载doc文件
* @param dataMap 模板数据
* @param template 模板
* @throws Exception 异常
*/
public static void exportWord(Map dataMap,Template template,OutputStream out) throws Exception {
template.binding(dataMap);
template.renderTo(out);
}
/**
* 设置让浏览器弹出下载对话框的Header
* @param response web响应
* @param fileName 文件名
*/
public static void setFileDownloadHeader(HttpServletResponse response, String fileName) {
try {
// 中文文件名支持 ContentType 根据下载的文件不同而不同
String encodedFileName = URLEncoder.encode(fileName, "UTF-8");
response.setContentType("application/x-xls");
response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName);
} catch (UnsupportedEncodingException e) {
LOGGER.error(e.getLocalizedMessage(),e);
}
}
/**
* 将模板渲染到指定文件
* @param filePath 文件全路径
* @param dataMap 数据
* @param templateName 模板名称
* @throws Exception 异常
*/
public static void renderToFile(String filePath,Map dataMap,String templateName) throws Exception{
Template template =getTemplate(templateName);
template.binding(dataMap);
try ( FileOutputStream fileOutputStream = new FileOutputStream(filePath)) {
template.renderTo(fileOutputStream);
}
}
}
在controller层或者service层,将查询出的数据放进map中,传递给模板;然后写给流导出。
Map data = new HashMap<>();
List
无论是生成word 还是 excel,都是通过一样的思路:
注:如果只是需要动态的替换掉某些位置的值,只需要在制作模板的时候设置个好记的初始值,生成另存为xml后打开替换为相应的变量即可;如果是需要动态的填充列表或者段落,道理也是一样,唯一需要注意的是,在xml里面找到预设值的地方后,需要找到合适的xml节点(循环节点),可通过生成测试是否正确,例如 word模板里,
http://ibeetl.com
使用文档 http://ibeetl.com/guide/#/beetl
在项目中遇到过需要编写 各种类型的统计jsp页面,根据选择的不同类型的条件以及限定条件,展示不同的统计页面以及筛选条件项;因为页面是根据选择的统计方式而变化的,所以编写固定的jsp页面就很难实现需求。
这时候找到的解决方法就是:制作jsp生成模板,模板内根据传入的参数进行代码判断,生成不同的元素,执行不同的查询和展示;即动态的生成 ‘代码页面’。后续可以加上一些判断,如将生成的jsp页面进行有效的命名,跳转页面的时候进行判断,如果已经生成(生成路径下存在 xx.jsp),则直接返回jsp路径,否则就先调用通过模板生成页面的方法,再返回jsp路径;
<${jsp_char_start} page language="java" pageEncoding="UTF-8"${jsp_char_end}>
<${jsp_char_start} taglib prefix="ta" tagdir="/WEB-INF/tags/tatags"${jsp_char_end}>
<${jsp_char_start} include file="/ta/inc.jsp"${jsp_char_end}>
<%
var length = report_item.~size;
var is_4 = length%4==0;
%>
<% if(length>0){%>
<%
for (item in report_item){
%>
<%
if (item.condition_type=='select' && item.condition_mb == '2'){
%>
<%
}
else {
var dataTypeStr = item.condition_type;
var selectType = item.condition_mb;
var dateCondition = item.condition_date;
var taType = "text";
var collectionValue;
var selectCol = "sqlType";
var dateShowType = '';
if(dataTypeStr == "date"){
taType = "date";
if(item.condition_date!='' && item.condition_date != 'date'){
var words = strutil.split(dateCondition,"_");
if(array.contain(words,"issue")){
dateShowType = ' issue ="true"';
}else{
dateShowType = words[0]+'="true"' ;
}
}
}
if(dataTypeStr == "number"){
taType = "number";
}
if(dataTypeStr == "select" && selectType == '1'){
taType = "selectInput";
selectCol = "colType";
collectionValue = ' collection="'+item.condition_code+'"' ;
}
if(dataTypeStr == "select" && selectType == '0'){
taType = "selectInput";
}
%>
<%
}
}
%>
<%}%>
<% if(length==0){%>
<%}%>
<${jsp_char_start} include file="/ta/incfooter.jsp"${jsp_char_end}>
注:导出生成后的word或者excel,一般常见的问题有:wps可以正常打开而office却打开报错。
一.制作excel模板生成的 xls wps可以打开,office打开报错
参考博文 https://blog.csdn.net/klchht/article/details/52218203
解决测试:
二. 生成word数据 设置 ContentType 错误, response.setContentType;需要针对导出 word、还是xls 选择不同的ContentType,可以网上搜索
要点: