【干货】java使用xml方式快速生成word

  • 为什么不使用POI呢?
  • 如何使用
  • 生成word的代码


为什么不使用POI呢?

对于这个问题,我想大家都有共同的见解:POI对于word的支持实在是太差了!
在我参与的一个项目中,我负责做一个根据模板文件,从数据库中提取相应的数据,生成一个word文件的模块,但是在我使用poi时候,我发现我没有办法将文字和所其中穿插的图片放到同一个模板标签中,所以我第一想法就是:先替换标签,再填充数据呗!可是通过实际验证,发现我的想法错了。

在第一步替换新的模板标签的时候,我发现,它们依然是隶属于同一Paragraph的,而所带来的问题就是,填充数据的时候,虽然能将文字和图片填充进去,但是他们是隶属于同一Paragraph,所以最终结果就是格式混乱,所有文字都在图片的上边。

那么我只要把每个标签都放在一个新的段落,就可以了啊!怎么弄呢。。。有了!在word中,换行就是一个新的段落了啊!!nice,这就开始做!换行好像是\n,试一下,不行!\r\n,不行,在wps中,\r\n可以被解析为换行,但是在office word中,却不行!那怎么办,要不直接添加一个换行吧!

不知所措的我,查了查百度,有人说通过在字符串中添加 (char)11 就可以换行了,试了一下,还是不行。。。再百度一下,poi对word有软回车和硬回车!!!nice!!

但是在实际撸码中,才发现,这俩好像对于我的需求没啥用啊。。。

对POI心灰意冷的我,发现了,可以通过xml来生成word … …

如何使用

首先,就是在制作模板了,打开我手上的word模板,另存为xml格式!对于如何制作,大体上我是以下的思路:

  1. 比对空文档和空文档添加一个文字段落,通过比较找出段落的样式
  2. 比对空文档和空文档添加一个图片段落,通过比较找出图片段落的格式
  3. 比对空文档和插入两个段落,通过比较找出多个段落的排列结构

首先找到文字的段落吧!
通过比对,发现,在xml中,文字的段落是如下格式的:

<w:r>
	<w:rPr>
		<w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast" />
		<w:lang w:val="EN-US" w:fareast="ZH-CN" />
	</w:rPr>
	<w:t>其实,在生活中,人们。。。</w:t>
</w:r>

哈哈,找到文字啦!!
那么如果有多个段落呢???再找找看!!!

<w:p>
	<w:pPr>
		<w:ind w:right="340" w:right-chars="162" w:first-line="420" />
		<w:rPr>
			<w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast" />
			<w:lang w:val="EN-US" w:fareast="ZH-CN" />
		</w:rPr>
	</w:pPr>
	<w:r>
		<w:rPr>
			<w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast" />
			<w:lang w:val="EN-US" w:fareast="ZH-CN" />
		</w:rPr>
		<w:t>其实,在生活中,人们。。。</w:t>
	</w:r>
</w:p>

<w:p>
	<w:pPr>
		<w:ind w:right="340" w:right-chars="162" w:first-line="420" />
		<w:rPr>
			<w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast" />
			<w:lang w:val="EN-US" w:fareast="ZH-CN" />
		</w:rPr>
	</w:pPr>
	<w:r>
		<w:rPr>
			<w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast" />
			<w:lang w:val="EN-US" w:fareast="ZH-CN" />
		</w:rPr>
		<w:t>还有很多的事情</w:t>
	</w:r>
</w:p>

我去!不对啊!怎么跟我刚才的不一样???原来, <w:p>标签才是word中代表一个段落的标签!! 所以之后的寻找也明确了,重点看<w:p>!!
然后通过比对,我也找到了图片的标签(过程就省略了,直接上标签!):

<w:p>
<w:pPr>
	<w:ind w:right="340" w:right-chars="162" />
	<w:jc w:val="center" /><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:fareast="宋体" w:hint="fareast" />
	<w:lang w:val="EN-US" w:fareast="ZH-CN" />
	</w:rPr>
</w:pPr>
<w:r>
	<w:rPr>
		<w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:fareast="宋体" w:hint="fareast" />
		<w:lang w:val="EN-US" w:fareast="ZH-CN" />
	</w:rPr>
	<w:pict>
		<w:binData w:name="wordml://1.jpg">此处为图片的base64编码</w:binData>
		<v:shape id="图片 3" o:spid="_x0000_s1033" o:spt="75" alt="timg" type="#_x0000_t75" style="height:282.6pt;width:288pt;" filled="f" o:preferrelative="t" stroked="f" coordsize="21600,21600">
			<v:path />
			<v:fill on="f" focussize="0,0" />
			<v:stroke on="f" />
			<v:imagedata src="wordml://1.jpg" o:title="timg" />
			<o:lock v:ext="edit" aspectratio="t" />
			<w10:wrap type="none" />
			<w10:anchorlock />
		</v:shape>
	</w:pict>
</w:r>
</w:p>

注意:
1.图片中标明“此处为图片的base64编码”的位置,是用于存放图片经过base64编码后的字符串的!如何将图片编码为base64字符串,请自行百度(很简单)!
2. 关于两个图片的src “wordml://1.jpg”,其中的图片名称不可以重复!大家编码的时候注意更改一下!

现在,只需要通过把原先的段落位置改变为模板标签,通过对我上述的标签的拼接,然后替换掉模板标签,再将xml转换成word就大功告成啦!!!

生成word的代码

/**
	 * @Title: createWord   
	 * @Description: TODO  将数据添加到xml中,并生成word文件
	 * @param: map  需要替换数据的map
	 * @param: templateName  模板名称
	 * @param filePath      想要生成的文件的路径
	 * @param fileName      想要生成的文件的名字
	 * @return: String    返回生成文件地址
	 * @throws
	 */
	public static String createWord(Map map, String templateName, String filePath, String fileName) {  
	    try {  
	        Configuration configuration = new Configuration();  
	        configuration.setDefaultEncoding("UTF-8");  
	        // 默认将模板放置在com.wrg.temp包下
	        configuration.setClassForTemplateLoading(WordUtils.class, "\\com\\wrg\\temp\\");  
	        Template template = configuration.getTemplate(templateName);  
	        File outFile = new File(filePath + File.separator + fileName);  
	        if (!outFile.getParentFile().exists()) {  
	            outFile.getParentFile().mkdirs();  
	        }  
	        Writer out = new BufferedWriter(
			        		  new OutputStreamWriter(
			        				new FileOutputStream(outFile), "UTF-8"));  
	        template.process(map, out);  
	        out.flush();  
	        out.close();  
	        return filePath + File.separator + fileName;  
	    } catch (Exception e) {  
	        e.printStackTrace();  
	    }  
	    return null;  
	}

如果大家有什么问题,欢迎联系我啊!如果有什么问题,也希望大家批评指正!