1. 生成docx模板和xml模板
生成docx模板
按照项目需要生成固定格式的docx格式的模板。
为方便测试做了个简单的例子,docx模板的内容如下图:
生成xml模板
从docx模板中取出word/document.xml,由于docx属于zip格式,可以用winrar打开,如图:
除word文件夹外其它文件为基本配置文件,取出word/document.xml(存放word文件的文本内容)如下图:
需要把document.xml解压出来,修改里面需要从数据库导出的数据用freemarker的指令代替,例如${test} 同时可以用遍历函数
替换完成后相当于生成了xml模板
生成的Xml模板:
1. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2.
3. <w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 wp14">
4. <w:body>
5. <w:p w:rsidR="009175C2" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
6. <w:pPr>
7. <w:pStyle w:val="1"/>
8. <w:jc w:val="center"/>
9. <w:rPr>
10. <w:rFonts w:hint="eastAsia"/>
11. </w:rPr>
12. </w:pPr>
13. <w:bookmarkStart w:id="0" w:name="_GoBack"/>
14. <w:bookmarkEnd w:id="0"/>
15. <w:r>
16. <w:rPr>
17. <w:rFonts w:hint="eastAsia"/>
18. </w:rPr>
19. <w:t>
20. 这是一个测试Word</w:t>
21. </w:r>
22. </w:p>
23. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
24. <w:pPr>
25. <w:rPr>
26. <w:rFonts w:hint="eastAsia"/>
27. </w:rPr>
28. </w:pPr>
29. </w:p>
30. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
31. <w:pPr>
32. <w:rPr>
33. <w:rFonts w:hint="eastAsia"/>
34. </w:rPr>
35. </w:pPr>
36. </w:p>
37. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
38. <w:pPr>
39. <w:pStyle w:val="a3"/>
40. <w:numPr>
41. <w:ilvl w:val="0"/>
42. <w:numId w:val="1"/>
43. </w:numPr>
44. <w:ind w:firstLineChars="0"/>
45. <w:rPr>
46. <w:rFonts w:hint="eastAsia"/>
47. </w:rPr>
48. </w:pPr>
49. <w:r>
50. <w:rPr>
51. <w:rFonts w:hint="eastAsia"/>
52. </w:rPr>
53. <w:t>
54. ${test}</w:t>
55. </w:r>
56. <w:r>
57. <w:rPr>
58. <w:rFonts w:hint="eastAsia"/>
59. </w:rPr>
60. <w:t xml:space="preserve">
61. </w:t>
62. </w:r>
63. </w:p>
64. <#list students as s>
65. <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
66. <w:pPr>
67. <w:pStyle w:val="a3"/>
68. <w:numPr>
69. <w:ilvl w:val="0"/>
70. <w:numId w:val="1"/>
71. </w:numPr>
72. <w:ind w:firstLineChars="0"/>
73. <w:rPr>
74. <w:rFonts w:hint="eastAsia"/>
75. </w:rPr>
76. </w:pPr>
77. <w:r>
78. <w:rPr>
79. <w:rFonts w:hint="eastAsia"/>
80. </w:rPr>
81. <w:t>
82. ${s}
83. </w:t>
84. </w:r>
85. </w:p>
86. </#list>
87. <w:sectPr w:rsidR="005B5FB3" w:rsidRPr="005B5FB3">
88. <w:pgSz w:w="11906" w:h="16838"/>
89. <w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/>
90. <w:cols w:space="425"/>
91. <w:docGrid w:type="lines" w:linePitch="312"/>
92. </w:sectPr>
93. </w:body>
94. </w:document>
2. 利用freemarker填充数据并生成word文件
这里把数据库中的数据放到map中,把map和xml模板交给freemarker来生成(填充完数据)的xml具体实现方法如下:
1. package com.hannet.yigehui;
2.
3. import java.io.File;
4. import java.io.FileOutputStream;
5. import java.io.IOException;
6. import java.io.OutputStreamWriter;
7. import java.io.Writer;
8. import java.util.HashMap;
9. import java.util.Map;
10.
11.
12. import freemarker.template.Configuration;
13. import freemarker.template.Template;
14. import freemarker.template.TemplateException;
15.
16. public class XmlToExcel {
17.
18.
19. private static XmlToExcel tplm = null;
20. private Configuration cfg = null;
21.
22. private XmlToExcel() {
23. cfg = new Configuration();
24. try {
25. //注册tmlplate的load路径
26. cfg.setClassForTemplateLoading(this.getClass(), "/template/");
27. } catch (Exception e) {
28.
29. }
30. }
31.
32. private static Template getTemplate(String name) throws IOException {
33. if(tplm == null) {
34. tplm = new XmlToExcel();
35. }
36. return tplm.cfg.getTemplate(name);
37. }
38.
39. /**
40. *
41. * @param templatefile 模板文件
42. * @param param 需要填充的内容
43. * @param out 填充完成输出的文件
44. * @throws IOException
45. * @throws TemplateException
46. */
47. public static void process(String templatefile, Map param ,Writer out) throws IOException, TemplateException{
48. //获取模板
49. Template template=XmlToExcel.getTemplate(templatefile);
50. template.setOutputEncoding("UTF-8");
51. //合并数据
52. template.process(param, out);
53. if(out!=null){
54. out.close();
55. }
56. }
57. }
利用freemarker生成数据
调用freemarker中的process方法(上图中的方法)来填充数据。
例(用1中生成的xml为模板填充数据):
//xml的模板路径 template/test.xml
String xmlTemplate = "test.xml";
//填充完数据的临时xml
String xmlTemp = "d:\\temp.xml";
Writer w = new FileWriter(new File(xmlTemp));
//1.需要动态传入的数据
Map<String,Object> p = new HashMap<String,Object>();
List<String> students = new ArrayList<String>();
students.add("张三");
students.add("李四");
students.add("王二");
p.put("test", "测试一下是否成功");
p.put("students", students);
//2.把map中的数据动态由freemarker传给xml
XmlToExcel.process(xmlTemplate, p, w);
注:下文中会给出源码这里只是方便理解。
导出word文件
利用java的zipFile 和 ZipOutputStream 及zipFile.getInputStream() 来根据docx模板导出 (需要把word/document.xml文件替换成(填充完数据)的xml)具体操作流程如下:
1. package com.hannet.yigehui;
2.
3. import java.io.File;
4. import java.io.FileInputStream;
5. import java.io.FileNotFoundException;
6. import java.io.FileOutputStream;
7. import java.io.FileWriter;
8. import java.io.IOException;
9. import java.io.InputStream;
10. import java.io.Writer;
11. import java.net.URISyntaxException;
12. import java.util.ArrayList;
13. import java.util.Enumeration;
14. import java.util.HashMap;
15. import java.util.List;
16. import java.util.Map;
17. import java.util.zip.ZipEntry;
18. import java.util.zip.ZipException;
19. import java.util.zip.ZipFile;
20. import java.util.zip.ZipOutputStream;
21.
22. import freemarker.template.TemplateException;
23.
24. /**
25. * 其实docx属于zip的一种,这里只需要操作word/document.xml中的数据,其他的数据不用动
26. * @author yigehui
27. *
28. */
29. public class XmlToDocx {
30.
31. /**
32. *
33. * @param documentFile 动态生成数据的docunment.xml文件
34. * @param docxTemplate docx的模板
35. * @param toFileName 需要导出的文件路径
36. * @throws ZipException
37. * @throws IOException
38. */
39.
40. public void outDocx(File documentFile,String docxTemplate,String toFilePath) throws ZipException, IOException {
41.
42. try {
43. String fileName = XmlToDocx.class.getClassLoader().getResource("").toURI().getPath()+docxTemplate;
44.
45. File docxFile = new File(fileName);
46. ZipFile zipFile = new ZipFile(docxFile);
47. Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();
48. ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(toFilePath));
49. int len=-1;
50. byte[] buffer=new byte[1024];
51. while(zipEntrys.hasMoreElements()) {
52. ZipEntry next = zipEntrys.nextElement();
53. InputStream is = zipFile.getInputStream(next);
54. //把输入流的文件传到输出流中 如果是word/document.xml由我们输入
55. zipout.putNextEntry(new ZipEntry(next.toString()));
56. if("word/document.xml".equals(next.toString())){
57. //InputStream in = new FileInputStream(new File(XmlToDocx.class.getClassLoader().getResource("").toURI().getPath()+"template/test.xml"));
58. InputStream in = new FileInputStream(documentFile);
59. while((len = in.read(buffer))!=-1){
60. zipout.write(buffer,0,len);
61. }
62. in.close();
63. }else {
64. while((len = is.read(buffer))!=-1){
65. zipout.write(buffer,0,len);
66. }
67. is.close();
68. }
69. }
70. zipout.close();
71. } catch (URISyntaxException e) {
72. e.printStackTrace();
73. }catch (FileNotFoundException e) {
74. e.printStackTrace();
75. }
76. }
77. }
这里输出的文件即为完整的docx格式的word文件
//经过本人测试,也可直接将输出文件取.doc后缀,也可以打开与转换,且解决低版本jodconverter无法转换DOCX文件的问题
测试调用的代码:
1. public static void main(String[] args) throws IOException, TemplateException {
2.
3. //xml的模板路径*/*
4. String xmlTemplate = "test.xml";
5.
6. //设置docx的模板路径 和文件名
7. String docxTemplate = "template/test.docx";
8. String toFilePath = "d:\\test.docx";
9.
10. //填充完数据的临时xml
11. String xmlTemp = "d:\\temp.xml";
12. Writer w = new FileWriter(new File(xmlTemp));
13.
14. //1.需要动态传入的数据
15. Map<String,Object> p = new HashMap<String,Object>();
16. List<String> students = new ArrayList<String>();
17. students.add("张三");
18. students.add("李四");
19. students.add("王二");
20. p.put("test", "测试一下是否成功");
21. p.put("students", students);
22.
23. //2.把map中的数据动态由freemarker传给xml
24. XmlToExcel.process(xmlTemplate, p, w);
25.
26. //3.把填充完成的xml写入到docx中
27. XmlToDocx xtd = new XmlToDocx();
28. xtd.outDocx(new File(xmlTemp), docxTemplate, toFilePath);
转换成PDF
1)基于openoffice(跨平台、需安装openoffice、复杂格式有错位)
原理:
通过第三方工具openoffice,将word、excel、ppt、txt等文件转换为pdf文件
先安装openoffice软件(Windows或Linux有提供软件)
使用JODConverter的Java的OpenDocument 文件转换器API操作Office系列文件转换为PDF文件
优点:
转换效果比较好。是比较主流的做法
缺点:
服务器需要安装openoffice,比较负重
具体实现:
1.下载安装软件
1)Openoffice:Apache下的一个开放免费的文字处理软件
下载地址:http://www.openoffice.org/zh-cn/download/
2)JODConverter一个Java的OpenDocument 文件转换器,只用到它的jar包
下载地址:https://sourceforge.net/projects/jodconverter/files/JODConverter/
2.启动服务:
打开dos窗口,进入openoffice安装盘符,输入以下代码来启动服务:
soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard
3.Java实现操作转化:
Pom.xml依赖
<!-- https://mvnrepository.com/artifact/com.artofsolving/jodconverter-maven-plugin -->
<dependency>
<groupId>com.artofsolving</groupId>
<artifactId>jodconverter-maven-plugin</artifactId>
<version>2.2.1</version></dependency>
</dependency>
转换工具:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.artofsolving.jodconverter.DocumentConverter;
import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.converter.OpenOfficeDocumentConverter;
/**
* 利用jodconverter(基于OpenOffice服务)将文件(*.doc、*.docx、*.xls、*.ppt)转化为html格式或者pdf格式,
* 使用前请检查OpenOffice服务是否已经开启, OpenOffice进程名称:soffice.exe | soffice.bin
*/
public class Doc2HtmlUtil {
private static Doc2HtmlUtil doc2HtmlUtil;
/** * 获取Doc2HtmlUtil实例 */
public static synchronized Doc2HtmlUtil getDoc2HtmlUtilInstance() {
if (doc2HtmlUtil == null) {
doc2HtmlUtil = new Doc2HtmlUtil();
}
return doc2HtmlUtil;
}
/*** 转换文件成pdf */
public String file2pdf(InputStream fromFileInputStream, String toFilePath,String type) throws IOException {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String timesuffix = sdf.format(date);
String docFileName = null;
String htmFileName = null;
if(".doc".equals(type)){
docFileName = "doc_" + timesuffix + ".doc";
htmFileName = "doc_" + timesuffix + ".pdf";
}else if(".docx".equals(type)){
docFileName = "docx_" + timesuffix + ".docx";
htmFileName = "docx_" + timesuffix + ".pdf";
}else if(".xls".equals(type)){
docFileName = "xls_" + timesuffix + ".xls";
htmFileName = "xls_" + timesuffix + ".pdf";
}else if(".ppt".equals(type)){
docFileName = "ppt_" + timesuffix + ".ppt";
htmFileName = "ppt_" + timesuffix + ".pdf";
}else{
return null;
}
File htmlOutputFile = new File(toFilePath + File.separatorChar + htmFileName);
File docInputFile = new File(toFilePath + File.separatorChar + docFileName);
if (htmlOutputFile.exists())
htmlOutputFile.delete();
htmlOutputFile.createNewFile();
if (docInputFile.exists())
docInputFile.delete();
docInputFile.createNewFile();
/*** 由fromFileInputStream构建输入文件 */
try {
OutputStream os = new FileOutputStream(docInputFile);
int bytesRead = 0;
byte[] buffer = new byte[1024 * 8];
while ((bytesRead = fromFileInputStream.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
fromFileInputStream.close();
} catch (IOException e) {
}
// 连接服务
OpenOfficeConnection connection = new SocketOpenOfficeConnection(8100);
try {
connection.connect();
} catch (ConnectException e) {
System.err.println("文件转换出错,请检查OpenOffice服务是否启动。");
}
// convert 转换
DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
converter.convert(docInputFile, htmlOutputFile);
connection.disconnect();
// 转换完之后删除word文件
docInputFile.delete();
return htmFileName;
}
public static void main(String[] args) throws IOException {
Doc2HtmlUtil coc2HtmlUtil = getDoc2HtmlUtilInstance ();
File file = null;
FileInputStream fileInputStream = null;
file = new File("C:/Users/MACHENIKE/Desktop/xxx.doc");
fileInputStream = new FileInputStream(file);
coc2HtmlUtil.file2pdf(fileInputStream, "E:/360","doc");
}
}
简易实现:
public void createPdf(String docFileName) throws IOException{
String path = this.getSession().getServletContext().getRealPath("/")+"attachment/";
File inputFile = new File(path+"/doc/"+ docFileName + ".doc");
File outputFile = new File(path+"/pdf/"+docFileName + ".pdf");
// connect to an OpenOffice.org instance running on port 8100
OpenOfficeConnection connection = new SocketOpenOfficeConnection(8100);
connection.connect();
// convert
DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
converter.convert(inputFile, outputFile);
// close the connection
connection.disconnect();
本文由各个大神的博客总结,加上自身测试而成,可解决Freemaker生成的文档手机端不能打开,与转换成的PDF文档是XML格式问题。