需要用到的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模板:

java 编辑app java 编辑xml word_word转PDF

    2、模板中需要动态改变的内容,通过freemarker标签:${variableName}

    说明:variableName是需要动态添加内容的一个标志符号,可自由命名

    注意:freemarker标签:${variableName},不能再word文档中直接输入,需要通过记事本,或者其他notePad++ 文本编辑工具先写好,然后再复制到文档中,否则在转换为xml模板时会出问题。具体问题可百度查找

    模板样式(参考):

java 编辑app java 编辑xml word_Java xml转word_02

    3、处理将要替换的图片,通过xml编辑工具打开xml模板文件:(可使用notePad++打开)

        两种替换图片方式,一种是直接动态插入一张图片,另一种是从数据库中读取,并循环插入图片

        (1)直接动态插入单张图片:(编辑xml模板文件)

                每个位置在保存模板前,放置一张图片,通过notePad++ 打开xml模板文件,找到对应的图片,在xml模板中,图片会发生转换,以base64的编码形式存在,只要找到一长串的字符就能确定这是一张图片;

java 编辑app java 编辑xml word_word动态填充数据_03

        把红框中的内容(后面还有很长),删除掉,直接替换freemarker标签,另外一张图片同样操作,如图所示:

java 编辑app java 编辑xml word_word动态填充数据_04

    (2)需要循环插入图片:(编辑xml模板文件)

                循环插图,需要加入freemarker循环标签,xml模板中:一对 <tr></tr>,为一行;我的模板设置了表格,需要循环插入表格,那么需要将循环标签包围到图片所在的 tr 标签:

java 编辑app java 编辑xml word_word转PDF_05

图片中横线说明:

        (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 编辑app java 编辑xml word_word动态填充数据_06


二、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格式。

java 编辑app java 编辑xml word_Java xml转word_07

    (4)通过调用转换docx方法,将xml格式的doc转换为docx的word文档格式:    注意:此步骤,需要用到jacob的jar包,在进行此操作之前,需要将对应jacob包的dll文件,拷贝到系统盘的目录和jdk目目录下的 jre\lib\ 下面目前我测试的版本有:jacob-1.6.jar、jacob-1.14.3.jar1.6版本对应的dll : jacob-1.17-M2-x64.dll1.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); // 不保存待定的更改
			}
		}
    }