技术方案:
- 一、使用Freemarker + doc xml 文件导出 .doc文档
- 二、使用XDOCReport + Freemarker模板导出 .docx文档
一、使用Freemarker + doc xml 文件导出.doc文档
优缺点:使用简单,但是xml文档太长,模板.ftl文件维护困难,直接修改word xml文件语法困难
依赖:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
1、生成XML文件
新建.doc文档并另存为xml文档,打开文件格式化
2、修改文件后缀为.ftl并使用Freemarker 语法修改xml文件
3、使用Freemarker 模板引擎填充值,导出doc文件
<dependency>
<groupId>com.meix</groupId>
<artifactId>ia-commons-utils</artifactId>
</dependency>
二、使用XDOCReport + Freemarker模板导出 .docx文档
优缺点:模板文件为.docx文件,使用word域作为占位符,模板文件维护相对简单,支持复杂格式(插入图片...),但是由于word格式问题经常导致导出时报错,
依赖:
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.core</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document.docx</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId>
<version>2.0.2</version>
</dependency>
1、新建word docx文档,Ctrl + F9 新建域,编辑域,选择邮件合并,填写freemarker 语法
2、使用Freemarker 模板生成站位符
//生成待占位符的docx临时文档
String destFileName = XWPFDocumentUtils.processTemplate(classPath, template, tempFileName, root);
//docx文档处理富文本生成word文档
XWPFDocumentUtils.processRichTextArea(tempFileName, filePath, fileName, richTextMap, response);
public static String processTemplate(String templatePath, String template, String tempFileName, Map<String, Object> data) throws Exception{
File file = ResourceUtils.getFile(templatePath + template);
InputStream ins = new FileInputStream(file);
IXDocReport docReport = XDocReportRegistry.getRegistry().loadReport(ins, TemplateEngineKind.Freemarker);
IContext context = docReport.createContext();
context.putMap(data);
File tempFile = new File(tempFileName);
if (!tempFile.getParentFile().exists()) {
tempFile.getParentFile().mkdirs();
}
tempFile.createNewFile();
OutputStream out = new FileOutputStream(tempFile);
docReport.process(context, out);
ins.close();
out.close();
return tempFileName;
}
public static void processRichTextArea(String tempFileName, String filePath, String destFileName, Map<String, List<Map<String, Object>>> richTextMap, HttpServletResponse response){
try {
File wordFile = ResourceUtils.getFile(tempFileName);
InputStream insWord = new FileInputStream(wordFile);
OPCPackage srcPackage = OPCPackage.open(insWord);
XWPFDocument document = new XWPFDocument(srcPackage);
XWPFDocumentUtils.insertRichTextAreaDocx(document, richTextMap);
XWPFDocumentUtils.exportDocx(document, filePath, destFileName, response);
insWord.close();
} catch (Exception e){
log.error("XWPFDocumentUtils.processRichTextArea error: {}", e.getMessage(), e);
}
}
public static void insertRichTextAreaDocx(XWPFDocument document, Map<String, List<Map<String, Object>>> richTextMap) {
try {
//遍历多模块
for (Map.Entry<String, List<Map<String, Object>>> e: richTextMap.entrySet()) {
List<Map<String, Object>> richTextList = e.getValue();
// 如果需要替换多份富文本,通过Map来操作,key:要替换的标记
for (Map<String, Object> richText : richTextList) {
Map<String, String> styles = (Map<String, String>) richText.get("styles");
Iterator<Map.Entry<String, Object>> iterator = richText.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<String, Object> entry = iterator.next();
for (XWPFParagraph paragraph : document.getParagraphs()) {
if(paragraph.getText().trim().contains(entry.getKey())){
log.info("paragraph Text ==============================" + paragraph.getText());
}
if (entry.getKey().equals(paragraph.getText().trim())) {
HtmlResolver htmlResolver = new HtmlResolver();
if(entry.getValue() != null){
htmlResolver.resolveHtml(entry.getValue().toString(), document, paragraph, styles);
}
//删除占位符
if (!iterator.hasNext()) {
document.removeBodyElement(document.getPosOfParagraph(paragraph));
}
break;
}
}
}
}
}
} catch (Exception e) {
log.error("XWPFDocumentUtils insertRichTextAreaDocx error: {}", e.getMessage(), e);
}
}
3、处理占位符替换为内容,处理富文本
4、富文本处理
<dependency>
<groupId>com.meix</groupId>
<artifactId>ia-commons-utils</artifactId>
</dependency>


