使用word绘制出客户需要的那种结果,让客户确认结果,然后再制作。
打开这个ftl文件,然后复制里面的所有内容,去百度格式化一下
格式化网址: http://www.bejson.com/otherformat/xmlsort/
传统的web项目引入2个jar包:
freemarker-2.3.28.jar
sun.misc.BASE64Decoder.jar
maven项目这样写即可:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import sun.misc.BASE64Encoder;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/*******************************************
*word文档导出
*******************************************/
public class FreeMarkerUtil {
private static Logger log = Logger.getLogger(FreeMarkerUtil.class.toString());
private static final String ENCODING = "UTF-8";
private static Configuration cfg = new Configuration();
//初始化cfg
static {
//我们要将刚刚创建成的ftl格式的文件放到这个路径下
cfg.setClassForTemplateLoading(FreeMarkerUtil.class, "/templates/XXXXX/freemaker");
// setEncoding这个方法一定要设置国家及其编码,不然在ftl中的中文在生成html后会变成乱码
cfg.setEncoding(Locale.getDefault(), ENCODING);
// 设置对象的包装器
cfg.setObjectWrapper(new DefaultObjectWrapper());
// 设置异常处理器,这样的话就可以${a.b.c.d}即使没有属性也不会出错
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
}
//获取模板对象
public static Template getTemplate(String templateFileName) throws IOException {
return cfg.getTemplate(templateFileName, ENCODING);
}
/**
* 据数据及模板生成文件
* @param data Map的数据结果集
* @param templateFileName ftl模版文件名
* @param outFilePath 生成文件名称(可带路径)
*/
public static File createFile(Map<String, Object> data, String templateFileName, String outFilePath) {
Writer out = null;
File outFile = new File(outFilePath);
try {
// 获取模板,并设置编码方式,这个编码必须要与页面中的编码格式一致
Template template = getTemplate(templateFileName);
if (!outFile.getParentFile().exists()) {
outFile.getParentFile().mkdirs();
}
out = new OutputStreamWriter(new FileOutputStream(outFile), ENCODING);
// 处理模版
template.process(data, out);
out.flush();
log.info("由模板文件" + templateFileName + "生成" + outFilePath + "成功.");
} catch (Exception e) {
log.error("由模板文件" + templateFileName + "生成" + outFilePath + "出错");
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
log.error("关闭Write对象出错", e);
e.printStackTrace();
}
}
return outFile;
}
//获得图片的base64码
public static String getImageBase(String src) throws Exception {
if (src == null || src == "") {
return "";
}
File file = new File(src);
if (!file.exists()) {
return "";
}
InputStream in = null;
byte[] data = null;
try {
in = new FileInputStream(file);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
}
public static void main(String[] args) {
try {
Map<String, Object> data = new HashMap<String, Object>();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date=new Date();
String current=sdf.format(date);
data.put("dqn", current.substring(0,4));
createFile(data, "zhuohao.ftl", "C:/Users/Administrator/Desktop/test.doc");
} catch (Exception e) {
e.printStackTrace();
}
}
}
js前端携带查询表单的查询参数(与列表的数据要一致)到后台获取数据
js代码:
goToWord_zkz: function(){
var self = this;
window.location.href = baseUrl + "xxxCont/xxxxxForWord?dclx=zkzdy&pcid=" + self.queryForm.pcid
+ "&xmtab="+self.queryForm.xmtab
+"&zytab="+self.queryForm.zytab
+ "&sftcs=" + self.queryForm.sftcs
+ "&bklb=" + self.queryForm.bklb
+ "&sfxstp="+self.queryForm.sfxstp;
self.$Message.success({
background: true,
duration: 10,
content: "如果数据较多,请耐心等待,不要重复点击"
});
},
java代码:
/**
* 打印导出
* @param dclx 导出类型:zhdy桌号打印、dzddy对照单打印、zkzdy准考证打印
*/
@RequestMapping(value = "xxxxxForWord")
public void xxxxxForWord(HttpServletRequest request, HttpServletResponse response, HttpSession session,Model model, ZkzwhEntity zzkzwhEnt, String dclx) throws IOException {
Map<String, Object> data = new HashMap<String, Object>();
data.put("currNd", DateUtil.CurrentTime("yyyy"));
List<ZkzwhEntity> xslist = zkzwhService.queryXsList_word(zzkzwhEnt);//new PageInfo(zkzwhService.queryXsList(bkshPage));
data.put("xslist", xslist);
// 先下载到服务器
String outPath = File.separator + "upload" + File.separator + "freeMaker" + File.separator + "temp";
File fl1 = new File(context.getRealPath(outPath));
fl1.mkdirs();
File fl2 = new File(context.getRealPath(outPath), user.getUsername()+dclx+".doc");// 下载到服务器的文件名称
if(!fl2.exists()) {
fl2.createNewFile();
}
String ftlName = "";//模板文件的全称
String docName = "";//导出文件的名字
FreeMarkerUtil fmu = new FreeMarkerUtil();
if("zhdy".equals(dclx)){//桌号打印
ftlName = "zhuohao.ftl";
docName = "年单招桌贴";
}else if("dzddy".equals(dclx)){//对照单打印
ftlName = "duizhaodan.ftl";
docName = "年单招对照单";
}else if("zkzdy".equals(dclx)){//准考证打印
ftlName = "zhunkaozheng.ftl";
docName = "年单招准考证";
}
fmu.createFile(data, ftlName, context.getRealPath(outPath) + File.separator + user.getUsername()+dclx + ".doc");
// 读取文件
if(fl2.exists()) {
InputStream ins = new FileInputStream(fl2);
BufferedInputStream bins = new BufferedInputStream(ins);// 放到缓冲流里面
OutputStream outs = response.getOutputStream();// 获取文件输出IO流
BufferedOutputStream bouts = new BufferedOutputStream(outs);
response.setContentType("application/x-download");// 设置response内容的类型
response.setHeader("Content-disposition" , "attachment;filename=" + new String((DateUtil.CurrentTime("yyyy")+ docName + ".doc").getBytes("GB2312"), "ISO-8859-1"));// 设置头部信息
int bytesRead = 0;
byte[] buffer = new byte[8192];
// 开始向网络传输文件流
while ((bytesRead = bins.read(buffer, 0, 8192)) != -1) {
bouts.write(buffer, 0, bytesRead);
}
bouts.flush();// 这里一定要调用flush()方法
ins.close();
bins.close();
outs.close();
bouts.close();
} else {
response.sendRedirect("下载失败");
}
}
开发过程中,一直都很顺利很快乐,但是最后的最后,还是遇到了问题,就是模板循环出来后,图片没有打印出来。
图片上的保存信息,意思就是图片资源没有找到。
百度了下,百度到如下信息,即id重复的问题,于是根据百度到的内容,我做出了修改:
<w:binData w:name="wordml://src_${xsObj.sfzh}.png" xml:space="preserve">${xsObj.zp_str!}w:binData>
<v:shape id="图片_${xsObj_index-1}" o:spid="_x0000_s1026${xsObj_sfzh}" type="#_x0000_t75${xsObj.sfzh}">
<v:imagedata src="wordml://src_${xsObj.sfzh}.png" o:title=""/>
v:shape>
注意:
1,${xsObj.zp_str!} 传递的是我从后台取出来的base格式的图片内容。
2,
3,word中存在多张 不同 的图片,那么在遍历图片时,id和name必须不同才可以,使用循环的下标来标记或者数据库中的id来标记,像我的文章中,我使用了身份证号作为id,就可以不重复。
我以为这就大功告成了,但是实际上,还是不行,我以为是我哪里写错了,我仔细检查了好几遍,确定代码没有写错,于是开始找数据的问题。
结果却是发现是数据来源的问题。
word转成的xml再转成ftl格式的数据后,它不支持前缀为 “data:image/jpg;base64,”,就是因为我的bas格式的图片中,我在存储时,故意拼接了这个字符,为了方便页面中 直接显示图片。但是在这里就必须没有这个 “data:image/jpg;base64,” 开头才可以。
因为别的地方还用到这个base格式的图像,所以不方便修改保存方法,于是在数据库中查询的时候截取了一下字符串即可:
完美!真香。