freemarker实现单元格动态合并-行合并
项目需求:项目中有个需求,需要将一些数据库中的数据根据需求导出,生成一个word,研究了一些技术,其中包括POI、freemaker,对比了一下实现过程及技术难度没最终使用了freemaker;
原始文件
效果:
实现过程大概分为三步,第一步:根据word文件做模板,修改模板,导出word。这里主要记录一下过程中遇到的一些情况。
一、制作模板
打开word文件,为要添加数据的地方打标签,,使用${key}的方式,key将来就是数据Map中的key,需要保持一致。然后另存为XML,因为word本质上就是个XML
二、处理XML文件
使用FirstObject XML Edito或者其他工具打开,我是用的是NotePad++,打开需要格式话一下,要不然没法看,打开主要是处理以下的情况:
这个是分开的,需要将中间的部分删除,处理后如下:
全部处理好之后,文件另存为ftl文件,这个就是我们制作完成的模板。
三、数据处理、打标签
这里主要说的是需要循环并且还需要合并单元格的情况。如果没有这些情况,直接打标签,封装数据就可以了。
第一个:list
主要是将数据封装到list中,list中是若干个Map,如图:
注意:list一定要放在要循环行开始的位置,及前一行结束的位置:
list结束位置:
四、数据准备
数据封装,数据放在Map中,这里Map中的key就是${key}中的key:
public void exportWord(HttpServletRequest request, HttpServletResponse response){
Map<String, Object> dataMap = new HashMap<>(16);
dataMap.put("total", "10");
List<Map<String,String>> list=new ArrayList<>( 16 );
for(int i=0;i<3;i++){
Map<String, String> listMap= new HashMap<>(16);
listMap.put( "no","10000"+i );
listMap.put( "name","test"+i );
listMap.put( "introduce","介绍"+i );
list.add( listMap );
}
dataMap.put( "whyc",checkList( list ) ); System.out.println(dataMap);
WordUtil.exportMillCertificateWord(response,dataMap,"test.docx","test.ftl");
} public List<Map<String, String>> checkList(List<Map<String, String>> list) {
String start = "<w:vMerge w:val='restart'/>";
String end = "<w:vMerge/>";
list.get(0).put("start", start);
for (int i = 1; i < list.size(); i++) {
list.get(i).put("end", end);
}
return list;
}
原则一、第一行数据只放"<w:vMerge w:val='restart'/>",从第二行开始,所有要合并的单元格放"<w:vMerge/>"。比如,我这个total这一行要合并,所以要这么处理:
public List<Map<String, String>> checkList(List<Map<String, String>> list) {
String start = "<w:vMerge w:val='restart'/>";
String end = "<w:vMerge/>";
list.get(0).put("start", start);
for (int i = 1; i < list.size(); i++) {
list.get(i).put("end", end);
}
return list;
}
数据输出是这样的:
原则二、编辑ftl模板文件,start和end的位置,一定放在行循环开始的<w:tcPr>中:
最后是文件导出及下载的代码:
package com.thupdi.project.utils; import freemarker.template.Configuration;
import freemarker.template.Template; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Map; /**
* @Author wangshuaijun
* @Date 2019/7/18 13:59
* @Version 1.0
*/
public class WordUtil {
private static Configuration configure; static {
configure= new Configuration();
configure.setDefaultEncoding("utf-8");
// configure.setClassForTemplateLoading(WordUtil.class,"com/thupdi/project/templates");
try {
configure.setDirectoryForTemplateLoading(new File( "D:/develop/IJWorkspaces/fzlsmc/src/main/resources/wordteplate" ));
} catch (IOException e) {
e.printStackTrace();
}
}
public WordUtil() {
super();
} /**
* 根据Doc模板生成word文件
* @param fileName 文件名称
* @throws IOException
*/
public static void exportMillCertificateWord(HttpServletResponse response,
Map<String,Object> map, String fileName, String ftlFile){ Template freemarkerTemplate = null;
try {
freemarkerTemplate = configure.getTemplate(ftlFile);
} catch (IOException e1) {
e1.printStackTrace();
} File file = null;
InputStream fin = null;
ServletOutputStream out = null;
try {
// 调用工具类的createDoc方法生成Word文档
file = createDoc(map,freemarkerTemplate,fileName);
fin = new FileInputStream(file); response.setCharacterEncoding("utf-8");
response.setContentType("application/msword");
// 设置浏览器以下载的方式处理该文件名
fileName = fileName + ".doc";
response.setHeader("Content-Disposition", "attachment;filename="
.concat(String.valueOf( URLEncoder.encode(fileName, "UTF-8")))); out = response.getOutputStream();
byte[] buffer = new byte[512]; // 缓冲区
int bytesToRead = -1;
// 通过循环将读入的Word文件的内容输出到浏览器中
while((bytesToRead = fin.read(buffer)) != -1) {
out.write(buffer, 0, bytesToRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fin != null) {
try {
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 删除临时文件
if (file != null) {
file.delete();
}
}
}
/**
* 生成word
* @param dataMap
* @param template
* @param fileName
* @return
*/
private static File createDoc(Map<?, ?> dataMap, Template template,String fileName) {
File f = new File(fileName);
Template t = template;
try {
// 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开
Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");
t.process(dataMap, w);
w.close();
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
return f;
}
}
Maven依赖:
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
freemarker实现单元格动态合并-行合并的更多相关文章
- 原创:用VBA实现将鼠标选择的单元格按照指定格式合并并复制到剪切板
原创:用VBA实现将鼠标选择的单元格按照指定格式合并并复制到剪切板 一.主要实现以下功能:1.用鼠标选择单元格(可谓连续单元格,也可为不连续的)后,按照要求格式“证件号码:+选定内容+“,”+”选定内 ...
- DataGridView合并单元格(多行多列合并)
一.点击在拖入的显示控件(TreeList)右上方的箭头,在Treelist任务中选择数据源,添加项目数据源,依次选择数据库.数据集,新建连接,浏览选择数据库(*.mdb),依次点击 下一步,选择“表 ...
- easyUI datagarid单元格动态合并
第二列根据第一列合并,第三列根据第二列合并.层级关系. /* * tableID表格的id * colList要合并的字段例如:"overcount,totalcount" */ ...
- Excel单元格内容拆分、合并
例:如何将EXCEL单元格A1中的“1-2-1”,在B1.C1.D1单元格中分别显示”1“.”2“.”1“.方法一: 在B1中输入“=mid(A1,1,1)”在C1中输入“=mid(AI,3,1)”在 ...
- Excel怎么把两个单元格中的文字合并到一个单元格中
使用&符号,可以将字符串和单元格中的内容拼接起来
- C# 对Excel 单元格格式, 及行高、 列宽、 单元格边框线、 冻结设置
一.对行高,列宽.单元格边框等的设置 这篇简短的文字对单元格的操作总结的比较全面,特此转载过来. private _Workbook _workBook = null; private Workshe ...
- 在DBGrid中,单击单元格选择整行,双击又可编辑单元格
在设计过程中,有时候数据较大量,field 较多的时候,只是点击单元格可能会对某个field的数据误操作(如数据错行),为此才会想到这个问题,解决办法如下:点击单元格就改当前行颜色. 首先DBGRID ...
- excel VBA返回选中单元格区域的行数、列数,以及活动单元格的行号和列号
Private Sub Worksheet_SelectionChange(ByVal Target As Range) '可以直接sub(),不然选择就会触发vba Dim rows_coun ...
- 【VBA】点击工作簿中的每个单元格,整行整列变色
需求描述 在点击单元格的时候,单元格所在行与列都变色显示 实现方案 打开Excle表格,按住ALT+F11 双击上图中的ThisWorkbook Private Sub Workbook_SheetS ...
随机推荐
- eclipse 插件编写(二)
上篇文章简单写了下怎么新建一个eclipse插件工程,这次写一下怎么在上次的工程中添加几个菜单,如菜单栏菜单.工具栏菜单.右键菜单等. 创建一个完成的菜单需要了解三个扩展点,即menus.comman ...
- [Erlang-0015][Lager] Erlang日志框架Lager简析
项目地址:https://github.com/basho/lager (欢迎任何形式的转载,但请务必注明出处:http://www.cnblogs.com/liangjingyang)
- window8 飘带与页面切换效果
演示效果如下 用鼠标点击滑动试试就能看到效果了 ^_^ iscroll 不仅可以做到自然滚动条的效果,看官方文档还可以用来做页面切换的效果,很好很强大. 所以我结合流行的飘带元素做了个简单的例子.. ...
- 基于QT的在线打字练习软件助手(C/S模型)good
简介 通过基于QT中QTcpServer和QTcpSocket以及UI编程,实现了基于TCP协议的C/S模型在线打字练习软件助手,服务端处理各客户端打字数据,以及显示在线打字客户列表即实时更新打字 ...
- 国人Web前端开发必备干货,一个完美支持IE6在内所有浏览器的CSS框架
摘要: 企户动CSS框架是一个能够完美支持IE6~7在内的所有浏览器的 HTML&CSS 前端框架!给Web开发提供了自适应宽度的百分比多列网格,以及已语义化和结构化的标题.段落.列表.表格. ...
- C# — WinForm TCP连接IPv4和IPv6的判断
大家都知道, XP系统默认使用的是IPv4格式的IP地址, 而Win7系统默认使用的是IPv6格式的IP地址. 所以有些关于TCP连接的WinForm系统,在XP下可能运行正常,但在Win7下却不能运 ...
- Tuxera NTFS 2018 for Mac中文破解版 U盘读写软件-让你的Mac支持NTFS
下载链接(复制到浏览器下载):http://h5ip.cn/TLMc 软件介绍 给大家带来一款苹果Mac上如何使用U盘读写的软件,Tuxera NTFS 2018 for Mac中文破解版,Mac O ...
- DOM模型-属性操作
HTML 元素包括标签名和若干个键值对,这个键值对就称为"属性"(attribute)."属性"本身是一个对象(Attr对象),但是实际上,这个对象极少使用.一 ...
- java web 开发教程(1) - 开发环境搭建
勤拂拭软件系列教程 之 Java Web开发之旅(1) Java Web开发环境搭建 1 前言 工作过程中,遇到不少朋友想要学习jsp开发,然而第一步都迈不出,连一个基本的环境都没有,试问,如何能够继 ...
- vue-resource.js的get和post的正确用法
在网上看到人家写的vue-resource.js的get方法例子, new Vue({ el:'body', data:{ }, methods:{ get:function(){ this.$htt ...