基于freemarker实现excel的模板导出
这里简单介绍一下使用freemarker的好处,适用于复杂的模板导出功能,并且还可以保持原有模板的一些方法。
第一步:添加依赖
我的项目是基于maven,所以在pom.xml中添加一下依赖就可以了:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
第二步:把excel另存为xml
在这里我要说一下,excel在另存为xml时里面的图片会丢失,它并不会像word那样生成一个占位符。所以我这个导出的excel是没有图片的。言归正传,这就是我另存为的xml的样例,其中row为行,cell为单元格
第三步:把要插入的数据使用freemarker的标签----${}替换
看到这里的${},是不是觉得它很像jstl标签,它的用法确实和jstl差不多,jstl是获取session中的值,而他是获取dataMap中的值;而且都是可以循环的便利的,它的便利标签就是
<#list list as item><#--其中list为你后台在dataMap中的key-->
<#if item.detaildata??><#--判断是否为空,如果不为空则显示内容-->
<#--${item.detaildata}是获取list的子元素的值-->
<Cell ss:StyleID="s37"><Data ss:Type="String">${item.detaildata}</Data></Cell>
<#else><#--否则显示空的单元格-->
<Cell ss:StyleID="s37"/>
</#if>
</#list>
我想很多人还是不清楚我讲的,可能确实是我的语言组织能力不好,不多说直接上截图,简洁明了
第四步:把xml的后缀名改成ftl,并且在webapp中新建template,把xxx.ftl,放到里面
好了基本的操作已经做完了,下面就是后台代码了。因为我的项目是MVC的设计理念并没有使用任何框架,所以把后台分为了四个层面:数据库对象序列化(dto),数据库访问层(DAO),数据处理层(service),控制层(servlet),算了还是用一个简单的例子吧,我开发的项目可能就要从头讲了。废话不多说先上service的代码,是一个封装好的类,可以直接调用的:
public class ExportExcelService {
public static void export(String templatePath, Map<String, Object> dataMap,
String buildFile, String newName,HttpServletRequest request,HttpServletResponse response) {
try {
ServletContext application = request.getSession().getServletContext();
Configuration configuration = new Configuration();
configuration.setDefaultEncoding("utf-8");
String path = application.getRealPath("template");
// 此处是本类Class.getResource()相对于模版文件的相对路径
configuration.setDirectoryForTemplateLoading(new File(path));
Template template = null;
File outFile = new File(buildFile);
Writer writer = null;
template = configuration.getTemplate(templatePath);
template.setEncoding("utf-8");
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(outFile), Charset.forName("utf-8")));// 此处为输
template.process(dataMap, writer);
writer.flush();
writer.close();
// return true;
// 设置response的编码方式
response.setContentType("application/x-msdownload");
// 设置附加文件名
response.setHeader("Content-Disposition", "attachment;filename="
+ new String(newName.getBytes("utf-8"), "iso-8859-1"));
// 读出文件到i/o流
FileInputStream fis = new FileInputStream(outFile);
BufferedInputStream buf = new BufferedInputStream(fis);
byte[] b = new byte[1024];// 相当于我们的缓存
long k = 0;// 该值用于计算当前实际下载了多少字节
// 从response对象中得到输出流,准备下载
OutputStream myout = response.getOutputStream();
// 开始循环下载
while (k < outFile.length()) {
int j = buf.read(b, 0, 1024);
k += j;
// 将b中的数据写到客户端的内存
myout.write(b, 0, j);
}
// 将写入到客户端的内存的数据,刷新到磁盘
myout.flush();
myout.close();
} catch (Exception e) {
e.printStackTrace();
// return false;
}
}
}
然后我们在servlet中调用这个类中的方法,重点就看我标黄的代码,这个是自己生成的list,而不是通过其他代码生成的,标蓝色的则是单一的键值对,红色代码为如果导出的数据不是模板的7天数据,则自动补充不足的空数据,以完成模板显示空单元格如下:
@WebServlet("/excelExport")
public class exportExcelServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uz=new String(URLDecoder.decode(req.getParameter("uz"),"utf-8"));
ReimburseService res=new ReimburseService();
String[] data=uz.split(",");
String uname=uz.split(",")[1];
String weeknumber=uz.split(",")[0];
Model model=res.querydetail(uname, weeknumber);
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("username",uz.split(",")[2]);
dataMap.put("writedata",uz.split(",")[3]);
dataMap.put("appartment",uz.split(",")[4]);
List list=(List) model.getData();
for(int i=list.size();i<7;i++){
ReimburseDetail rd=new ReimburseDetail();
rd.setDetaildata("");
rd.setHotelfare("");
rd.setLocation("");
rd.setLongfare("");
rd.setOtherfare("");
rd.setShortfare("");
rd.setProject("");
rd.setFaredescription("");
list.add(rd);
}
//对list进行排序
for(int i=0;i<list.size();i++){
ReimburseDetail rdl=(ReimburseDetail)list.get(i);
try {
if(!rdl.getDetaildata().equals("")){
int b=dayForWeek(rdl.getDetaildata());
if(b-1!=i){
Object objA= list.get(i);
list.set(i, list.get(b-1));
list.set(b-1, objA);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
dataMap.put("list",list);
List ls=new ArrayList();
String[] num={"A","B","C","D","E","F","G"};
String[] Formula={"-9","-10","-11","-12","-13","-14","-15"};
for(int i=0;i<num.length;i++){
Map<String,Object> map = new HashMap<String,Object>();
map.put("num",num[i]);
ReimburseDetail r=(ReimburseDetail)list.get(i);
map.put("faredescription",r.getFaredescription());
map.put("Formula", Formula[i]);
map.put("col", i+1);
ls.add(map);
}
dataMap.put("ls",ls);
ServletContext application = req.getSession().getServletContext();
/*********输出图片未解决********************/
String a=application.getRealPath("imgs");
String img = getImageStr(a+"/ceit.png");
dataMap.put("image", img);
/***********************************/
String templateName = "excel.ftl"; //模板名称
java.text.DateFormat format1 = new java.text.SimpleDateFormat("yyyy-MM-dd");
String exportexcel = application.getRealPath("template")+format1.format(new Date())+ ".xls";
ExportExcelService.export(templateName, dataMap, exportexcel, "每周报销单("+format1.format(new Date())+").xls",req,resp);
}
/***************获取日期是星期几*************************/
public static int dayForWeek(String pTime) throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd" );
Calendar c = Calendar.getInstance();
c.setTime(format.parse(pTime));
int dayForWeek = 0 ;
if (c.get(Calendar.DAY_OF_WEEK) == 1 ){
dayForWeek = 7 ;
}else {
dayForWeek = c.get(Calendar.DAY_OF_WEEK) - 1 ;
}
return dayForWeek;
}
/**************将图片转成base64********************/
public String getImageStr(String imgFile) {
InputStream in = null;
String da="";
byte[] data = null;
try {
in = new FileInputStream(imgFile);
data = new byte[in.available()];
in.read(data);
in.close();
da=new String(Base64.encodeBase64(data),"UTF-8");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return da;
}
}
好了,文章写的这里就圆满结束了。唯一的遗憾就是图片导出还没有解决,等解决了再来补充,当然如果是导出word模板是可以导出图片的因为再把带有图片的word转xml时会生成这个标签------》<w:binDataw: name="wordml://03000001.png" xml:space ="preserve">${image}</w:binData><v:shape id="_x0000_i1025" type="#_x0000_t75" style="width:414.75pt; height:319.5pt"> <v:imagedata src="wordml://03000001.png" o:title=""/></v:shape>关于这个网上有很多,可以多去逛逛。