需要用到的jar包:
commons-codec-1.10.jar
freemarker-2.3.21.jar
jacob-1.6.jar
实现思路:
1、先将word文档另存为 : Word 2003 XML文档(*.xml) 格式的模板文件;
2、通过Java程序,使用
freemarker.jar包转换为xml格式的word文档文件
3、使用jacob包,将xml格式的word文档文件,转换为docx格式的文档文件
具体实现步骤:
一、模板编辑
1、将word文档另存为xml模板:
2、模板中需要动态改变的内容,通过freemarker标签:${variableName}
说明:variableName是需要动态添加内容的一个标志符号,可自由命名
注意:freemarker标签:${variableName},不能再word文档中直接输入,需要通过记事本,或者其他notePad++ 文本编辑工具先写好,然后再复制到文档中,否则在转换为xml模板时会出问题。具体问题可百度查找
模板样式(参考):
3、处理将要替换的图片,通过xml编辑工具打开xml模板文件:(可使用notePad++打开)
两种替换图片方式,一种是直接动态插入一张图片,另一种是从数据库中读取,并循环插入图片
(1)直接动态插入单张图片:(编辑xml模板文件)
每个位置在保存模板前,放置一张图片,通过notePad++ 打开xml模板文件,找到对应的图片,在xml模板中,图片会发生转换,以base64的编码形式存在,只要找到一长串的字符就能确定这是一张图片;
把红框中的内容(后面还有很长),删除掉,直接替换freemarker标签,另外一张图片同样操作,如图所示:
(2)需要循环插入图片:(编辑xml模板文件)
循环插图,需要加入freemarker循环标签,xml模板中:一对 <tr></tr>,为一行;我的模板设置了表格,需要循环插入表格,那么需要将循环标签包围到图片所在的 tr 标签:
图片中横线说明:
(1)图片中因为有两个地方插入图片,所以需要将一个list对象中,要存两张图片的数据,
(2)<#list loopImageList as imgList> :loopImageList 可自由命名,但是在Java代码中动态填充的数据对象名要一致,imageList 作为一个遍历的对象名,等于将loopImageList的内容赋值给imageList ,获取对象中的属性值,通过imageList.attr 获取
(3)上面的 w:name= 后面是图片的名称,不能一致,必须存不同的文件名,所以,这个需要改动,否则一旦所有文件名称一致,则会出现问题。
(4)如果需要合并第一列的单元格,需要在tr加入标签:
具体合并方法:<#if imageList_index == 0><w:vmerge w:val="restart"/><#else><w:vmerge/></#if>
a、开始合并的行:<w:vmerge w:val="restart"/>
b、需要合并的行:<w:vmerge/>
上面的语句中,_index为imageList 的索引写法,因为需要合并,所以通过判断的方式,将第一个单元格作为合并起始行,其余需要合并的行需要添加结束标签。
(5)如果不需要合并,则不用添加(4)中的语句
二、Java后台代码实现
(1)创建一个封装图片对象的ImageBean类:
(由于list遍历中存了两张不同的图片,所以参数设计不太合理,可参考例子,自己进行优化)
package testXML;
/**
* 图片bean
*/
public class ImageBean {
// 图片1
private Integer img1Index;
private String imgName;
private String img1Width;
private String img1Height;
private String img1Base64;
// 图片2
private Integer img2Index;
private String img2Width;
private String img2Height;
private String img2Base64;
public ImageBean(Integer img1Index, String imgName, String img1Width,
String img1Height, String img1Base64, Integer img2Index,
String img2Width, String img2Height,
String img2Base64) {
this.img1Index = img1Index;
this.imgName = imgName;
this.img1Width = img1Width;
this.img1Height = img1Height;
this.img1Base64 = img1Base64;
this.img2Index = img2Index;
this.img2Width = img2Width;
this.img2Height = img2Height;
this.img2Base64 = img2Base64;
}
public Integer getImg1Index() {
return img1Index;
}
public void setImg1Index(Integer img1Index) {
this.img1Index = img1Index;
}
public String getImgName() {
return imgName;
}
public void setImgName(String imgName) {
this.imgName = imgName;
}
public String getImg1Width() {
return img1Width;
}
public void setImg1Width(String img1Width) {
this.img1Width = img1Width;
}
public String getImg1Height() {
return img1Height;
}
public void setImg1Height(String img1Height) {
this.img1Height = img1Height;
}
public String getImg1Base64() {
return img1Base64;
}
public void setImg1Base64(String img1Base64) {
this.img1Base64 = img1Base64;
}
public Integer getImg2Index() {
return img2Index;
}
public void setImg2Index(Integer img2Index) {
this.img2Index = img2Index;
}
public String getImg2Width() {
return img2Width;
}
public void setImg2Width(String img2Width) {
this.img2Width = img2Width;
}
public String getImg2Height() {
return img2Height;
}
public void setImg2Height(String img2Height) {
this.img2Height = img2Height;
}
public String getImg2Base64() {
return img2Base64;
}
public void setImg2Base64(String img2Base64) {
this.img2Base64 = img2Base64;
}
}
(2)将xml模板生成word文档(本质是xml):(代码中存在通用工具处理类)
package testXML;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
public class XmlTemplate2Word {
public static void main(String[] args) throws IOException, TemplateException{
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("title", "测试转换word文档");
dataMap.put("head", "头部信息");
dataMap.put("content", "主要内容主要内容主要内容");
dataMap.put("tableDescription", "表格描述内容");
// 构造一个list,存放图片相关信息
List<Object> list = new ArrayList<Object>();
// 将图片转为base64编码格式 (实际运用中可通过循环处理)
String imgBase64Str1 = getImgStr("C:/Users/admin/Desktop/签名/李燕萍.png");
String imgBase64Str2 = getImgStr("C:/Users/admin/Desktop/签名/李燕萍2.png");
String imgBase64Str3 = getImgStr("C:/Users/admin/Desktop/签名/彭玉婷.png");
String imgBase64Str4 = getImgStr("C:/Users/admin/Desktop/签名/琪.png");
String imgBase64Str5 = getImgStr("C:/Users/admin/Desktop/签名/婿丹.png");
String imgBase64Str6 = getImgStr("C:/Users/admin/Desktop/签名/杨诗敏.png");
list.add(new ImageBean(1,"图片1","101","35",imgBase64Str1,2,"101","35",imgBase64Str2));
list.add(new ImageBean(3,"图片2","101","35",imgBase64Str3,4,"101","35",imgBase64Str4));
list.add(new ImageBean(5,"图片3","101","35",imgBase64Str5,6,"101","35",imgBase64Str6));
dataMap.put("loopImageList", list);
// 模板文件路径:
String templetFilePath = "C:/Users/admin/Desktop/测试模板.xml";
// 目标文件存放路径
String targetFilePath = "C:/Users/admin/Desktop/测试模板(xml格式).doc";
// 将xml模板转换为后缀为doc文件,本质仍是属于xml
xml2XmlDoc(dataMap,templetFilePath,targetFilePath);
}
/**
* 将xml模板转换为后缀为doc文件,本质仍是属于xml
* @param dataMap 需要填充到模板的数据
* @param templetFilePath 模板文件路径
* @param targetFilePath 目标文件保存路径
* @throws IOException
* @throws TemplateException
*/
public static void xml2XmlDoc(Map<String,Object> dataMap, String templetFilePath, String targetFilePath) throws IOException, TemplateException{
// 将模板文件路径拆分为文件夹路径和文件名称
String tempLetDir = templetFilePath.substring(0,templetFilePath.lastIndexOf("/"));
// 注意:templetFilePath.lastIndexOf("/")中,有的文件分隔符为:\ 要注意文件路径的分隔符
String templetName = templetFilePath.substring(templetFilePath.lastIndexOf("/")+1);
// 将目标文件保存路径拆分为文件夹路径和文件名称
String targetDir = targetFilePath.substring(0,targetFilePath.lastIndexOf("/"));
String targetName = targetFilePath.substring(targetFilePath.lastIndexOf("/")+1);
Configuration configuration = new Configuration();
configuration.setDefaultEncoding("UTF-8");
// 如果目标文件目录不存在,则需要创建
File file = new File(targetDir);
if(!file.exists()){
file.mkdirs();
}
// 加载模板数据(从文件路径中获取文件,其他方式,可百度查找)
configuration.setDirectoryForTemplateLoading(new File(tempLetDir));
// 获取模板实例
Template template = configuration.getTemplate(templetName);
File outFile = new File(targetDir + File.separator + targetName);
//将模板和数据模型合并生成文件
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8"));
//生成文件
template.process(dataMap, out);
out.flush();
out.close();
}
/**
* 将图片转换成Base64编码 (优化:在web工程下,可通过工程路径获取流的方式获取图片)
* @param imgFile 图片路径
* @return
* @throws IOException
*/
public static String getImgStr(String imgFilePath) throws IOException{
//将图片文件转化为字节数组字符串,并对其进行Base64编码处理
InputStream in = null;
byte[] data = null;
//读取图片字节数组
in = new FileInputStream(imgFilePath);
data = new byte[in.available()];
in.read(data);
in.close();
return new String(Base64.encodeBase64(data));
}
}
(3)模板准备好,模板路径正确后,运行程序,得到输出文件:文件内容如下。表格内容进行了替换,图片也循环填充到文档中,但是,这个文档,本质仍然属于xml.格式文档,打开之后,点击另存为会发现是xml格式。
:
(4)通过调用转换docx方法,将xml格式的doc转换为docx的word文档格式:
注意:此步骤,需要用到jacob的jar包,在进行此操作之前,需要将对应jacob包的dll文件,拷贝到系统盘的目录和jdk目目录下的 jre\lib\ 下面
目前我测试的版本有:jacob-1.6.jar、jacob-1.14.3.jar
1.6版本对应的dll : jacob-1.17-M2-x64.dll
1.14.3版本对应的dll : jacob-1.14.3-x64.dll
我展示的展示的是64位操作系统的,具体32位的到网上查找,不懂的可评论留言我看到会回复
具体的资源文件jar包,可自行到网上找,或者到maven仓库中下载。
/**
* xml形式的doc文件转换为Docx格式
* @param sourcePath 被转换文件的路径
* @param targetPath 目标文件路径
* @return
* @author lixs
* @Date 2018年5月29日16:24:08
*/
public static void docToDocx(String sourcePath, String targetPath){
//Word.Application代表COM OLE编程标识,可查询MSDN得到
ActiveXComponent app = new ActiveXComponent("Word.Application");
//设置Word不可见
app.setProperty("Visible",false);
//调用Application对象的Documents属性,获得Documents对象
Dispatch docs = app.getProperty("Documents").toDispatch();
//Dispatch doc = Dispatch.call(docs,"Open",sourcePath,new Variant(false),new Variant(true)).getDispatch();
Dispatch doc = Dispatch.call(docs,"Open",sourcePath).getDispatch();
Dispatch.call(doc,"SaveAS",targetPath,12);
//关闭打开的Word文件
Dispatch.call(doc,"Close",false);
//关闭Word应用程序
app.invoke("Quit",0);
}
(5)有的需要将word文档转换为PDF文件,这里提供一个转PDF的通用方法,:
/**
* @Description: word文件转pdf文件
* @param sourcePath 被转换word文档路径
* @param targetPath 目标PDF文件路径路径
* @return boolean
* @date 2018年5月22日15:19:48
*/
public static boolean word2pdf(String sourcePath, String targetPath) {
ActiveXComponent app = null;
try {
app = new ActiveXComponent("Word.Application");
app.setProperty("Visible", false);
Dispatch docs = app.getProperty("Documents").toDispatch();
Dispatch doc = Dispatch.call(docs, "Open", sourcePath, false, true).toDispatch();
File tofile = new File(targetPath);
if (tofile.exists()) {
tofile.delete();
}
Dispatch.call(doc, "SaveAs", targetPath, 17); // word转PDF格式
Dispatch.call(doc, "Close", false);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
if (app != null) {
app.invoke("Quit", 0); // 不保存待定的更改
}
}
}