poi-tl根据word模板导出word、使用spring-thymeleaf模板生成html并通过docx4j把html转word,使用jxls根据excel模板导出excel(2)
thymeleaf
官网
https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
参考
https://blog.csdn.net/u010739551/article/details/81566987
Thymeleaf是用于Web和独立环境的现代服务器端Java模板引擎。
Thymeleaf的主要目标是将优雅的自然模板带到您的开发工作流程中—HTML能够在浏览器中正确显示,并且可以作为静态原型,从而在开发团队中实现更强大的协作。Thymeleaf能够处理HTML,XML,JavaScript,CSS甚至纯文本。
Thymeleaf的主要目标是提供一个优雅和高度可维护的创建模板的方式。 为了实现这一点,它建立在自然模板的概念之上,以不影响模板作为设计原型的方式将其逻辑注入到模板文件中。 这改善了设计沟通,弥合了前端设计和开发人员之间的理解偏差。
自动生成WORD文档并下载,导出WORD文档 thymeleaf
* 配置jxls后thymeleaf不好使 因thymeleaf默认找后缀为.html的模板 而jxls 指定了后缀为 .xls 所以不能使用Mode直接返回视图 需通过spring thymeleaf 把docx4j转word并下载
* 所以thymeleaf找不到相关模板 解决方案为demo2 通过docx4j 使 html 转word 并下载
docx4j
官网
https://www.docx4java.org/trac/docx4j
例子
https://github.com/plutext/docx4j/tree/master/docx4j-samples-docx4j
欢迎使用docx4j
docx4j是一个开源(ASLv2)Java库,用于创建和处理Microsoft Open XML(Word docx,Powerpoint pptx和Excel xlsx)文件。
它类似于Microsoft的OpenXML SDK,但适用于Java。docx4j使用JAXB创建内存中的对象表示形式。
它的重点是功能:如果文件格式支持它,则可以使用docx4j来实现。但是首先,您需要花一些时间来了解JAXB和Open XML文件结构
docx4j由Plutext Pty Ltd于2008年创建-将OpenXML4J用于OPC。Plutext仍然推动着该项目的发展,但是从那时起docx4j受益于许多个人的贡献。贡献者列在docx4j的pom.xml中。
docxj4简介
“现在有了docx4j,使用我设想的方法生成Word文档要容易得多。”
“过去一个月我一直在新产品上使用docx4j,我对docx4j的使用量印象深刻并感到感谢。”
“该库为您提供了从Java创建/加载/编辑/编写Word docx文档所需的一切,并附带了Maven仓库,在线Javadoc和不错的示例代码集。您还需要什么呢?:-
thymeleaf
1.导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency><!--docx4j start-->
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-ImportXHTML</artifactId>
<version>8.0.0</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
<version>8.0.0</version>
</dependency>
<!--docx4j start-->
2.模板
thymeleafDemo1.html模板
<html lang="zh_CN">
<head>
<meta charset="UTF-8"/>
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Hello report.</title>
</head>
<body>
<h1 data-th-text="${hello}">Hello report.</h1>
<br/>
<br/>
<img src="https://img-blog.csdnimg.cn/2022010622205164259.png" class="header-logo"/>
<br/>
<h1>base64Image写死图片base64</h1>
<img src=""
data-th-src="${base64Image}"
width="80px"
height="60px"/>
<br/>
<h1>imagData</h1>
<img src=""
data-th-src="${imagData}"
width="80px"
height="60px"/>
<br/>
<h1>imagData2</h1>
<img src=""
data-th-src="${imagData2}"
width="80px"
height="60px"/>
<br/>
<table>
<thead>
<tr>
<th>id</th>
<th>信息</th>
<th>日期</th>
<th>图片</th>
</tr>
</thead>
<tbody>
<tr data-th-each="hel : ${hellos}">
<th data-th-text="${hel.id}">id</th>
<th data-th-text="${hel.hello}">message</th>
<th data-th-text="${#dates.format(hel.date, 'yyyy-MM-dd HH:mm')}">date</th>
<th>
<!--<img src="https://img-blog.csdnimg.cn/2022010622205261527.png"-->
<!--data-th-src="${base64Image}"-->
<!--data-th-title="'title_' + ${hel.hello}" title="title"-->
<!--data-th-alt="'title_' + ${hel.hello}" alt="alt"-->
<!--width="80px"-->
<!--height="60px"/>-->
<img src=""
data-th-src="${base64Image}"
data-th-title="'title_' + ${hel.hello}" title="title"
data-th-alt="'title_' + ${hel.hello}" alt="alt"
width="80px"
height="60px"/>
</th>
</tr>
</tbody>
</table>
</body>
</html>
java
示例:自动生成WORD文档并下载,导出WORD文档 thymeleaf
* 配置jxls后thymeleaf不好使 因thymeleaf默认找后缀为.html的模板 而jxls 指定了后缀为 .xls 所以不能使用Mode直接返回视图 需通过docx4j 把html转word并下载
* 所以thymeleaf找不到相关模板 解决方案为demo2 通过docx4j 使 html 转word 并下载
package com.shan.mydemo.controller;
import com.shan.mydemo.domain.*;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.thymeleaf.context.Context;
import sun.misc.BASE64Encoder;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;
/**
* @author shanc
* @version 1.0
* jxls 案例
*/
@Controller
@RequestMapping("/thymeleafDemo")
public class ThymeleafDemoController {
@Autowired
private InlineImageWordGenerator docGenerator;
/**
* 作者: shanc
* 时间: 2021/4/19 11:07
* 描述:
* 配置jxls后thymeleaf不好使 因thymeleaf默认找后缀为.html的模板 而jxls 指定了后缀为 .xls 所以不能使用Mode直接返回视图
* 需通过docx4j 使 把html转word并下载
*/
@GetMapping("/demo1")
public String getUserList(Model mode) {
String base64Image = "https://img-blog.csdnimg.cn/2022010622205164259.png";
List<HelloFO> list=new ArrayList<>();
ThreadLocalRandom tl=ThreadLocalRandom.current();
IntStream.range(10,tl.nextInt(20,40)).mapToObj( i -> {
HelloFO build = HelloFO.builder()
.id(String.valueOf(i))
.hello("name-" + i)
.date(new Date()).build();
return build;
}
).forEach(list::add);
URL url = this.getClass().getClassLoader().getResource("static/default-image-placeholder-china-coal_800x600.png");
String path = url.getPath();
System.out.println(path);
byte[] data = null;
// 读取图片字节数组
try {
InputStream in = new FileInputStream(path);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
// 对字节数组Base64编码
BASE64Encoder encoder = new BASE64Encoder();
String imagData = encoder.encode(Objects.requireNonNull(data));
// 返回Base64编码过的字节数组字符串
System.out.println("本地图片转换Base64:" + imagData);
//照片转bast64
String imagData2 = ImageUtils.toBase64DataURL(url);
//写死图片base64
mode.addAttribute("base64Image", base64Image);
//mode.addAttribute("base64Image", imagData2);
//通过流转
mode.addAttribute("imagData", "data:image/png;base64,"+imagData);
//工具转
mode.addAttribute("imagData2", imagData2);
mode.addAttribute("hellos", list);
mode.addAttribute("hello","Hello report demo 1.");
return "thymeleaf/thymeleafDemo1";
}
/**
* 示例:自动生成WORD文档并下载,导出WORD文档 thymeleaf
* 配置jxls后thymeleaf不好使 因thymeleaf默认找后缀为.html的模板 而jxls 指定了后缀为 .xls 所以不能使用Mode直接返回视图 需通过spring thymeleaf 把html转word并下载
* 所以thymeleaf找不到相关模板 解决方案为demo2 通过docx4j 使 html 转word 并下载
* @param response response
* @throws IOException e
*
*/
@ApiOperation("示例:自动生成WORD文档并下载,导出WORD文档")
@GetMapping("/demo2")
public void demo2(HttpServletResponse response) throws IOException {
Context ctx =new Context();
List<HelloFO> list=new ArrayList<>();
ThreadLocalRandom tl=ThreadLocalRandom.current();
IntStream.range(10,tl.nextInt(20,40)).mapToObj( i -> {
HelloFO build = HelloFO.builder()
.id(String.valueOf(i))
.hello("name-" + i)
.date(new Date())
.build();
return build;
}
).forEach(list::add);
//通过流转
URL url = this.getClass().getClassLoader().getResource("static/default-image-placeholder-china-coal_800x600.png");
String base64Image = ImageUtils.toBase64DataURL(url);
ctx.setVariable("base64Image", base64Image);
ctx.setVariable("hellos", list);
ctx.setVariable("hello","Hello report demo 1.");
// set response file name
String fileName = "hello-report-2.docx";
HttpServletResponseUtils.setDownloadHeader(fileName, response);
// do generate doc file
docGenerator.generate("thymeleaf/thymeleafDemo2", ctx, response.getOutputStream());
}
@ApiOperation("示例:导出出题表导出")
@GetMapping("/reply-question-export")
public void replyQuestionExport(HttpServletResponse response) throws IOException {
// set variable: hello, as title
Context ctx = new Context();
ctx.setVariable("title", "申报高级工程师系列人员论文答辩出题表");
// dclrQuestionList
List<Map<String, String>> dclrQuestionList = new LinkedList<>();
ThreadLocalRandom random = ThreadLocalRandom.current();
IntStream.range(10, random.nextInt(10, 33))
.mapToObj(i -> {
Map<String, String> dcleQuestion = new LinkedHashMap<>();
dcleQuestion.put("orgName", "单位名称_" + i);
dcleQuestion.put("userName", "姓名_" + i);
dcleQuestion.put("paperTitleOrSummary", "论文题目内容摘要_" + i);
dcleQuestion.put("question1", "问题1_" + i);
dcleQuestion.put("question2", "问题2_" + i);
dcleQuestion.put("question3", "问题3_" + i);
dcleQuestion.put("question4", "问题4_" + i);
dcleQuestion.put("expertName", "专家姓名_" + i);
return dcleQuestion;
})
.forEach(dclrQuestionList::add);
ctx.setVariable("dclrQuestionList", dclrQuestionList);
// set response file name
String fileName = "reply-question-" + System.currentTimeMillis() + ".docx";
HttpServletResponseUtils.setDownloadHeader(fileName, response);
// do generate doc file
docGenerator.generate("thymeleaf/reply-question", ctx, response.getOutputStream());
}
/**
* 作者: shanc
* 时间: 2021/4/19 10:55
* 描述: jxls根据excel模板导出excel
*/
@GetMapping("/title-registry-form-excel")
public String titleRegistryFormExcel(Model model) {
String [] gender={"男","女"};
Date now = new Date();
model.addAttribute("org_name", "中国中煤能源集团有限公司");
model.addAttribute("year_yyyy", "2020");
model.addAttribute("series_name_short", "会计");
model.addAttribute("qualification_name", "高级会计师");
model.addAttribute("proj_level", "高级");
model.addAttribute("fill_form_user_name", "管理员");
model.addAttribute("telephone", "18888888888");
model.addAttribute("fill_form_date", TasDateTimeUtils.format(now, "yyyy年MM月dd日"));
List<Map<String,Object>> list =new ArrayList<>();
ThreadLocalRandom random =ThreadLocalRandom.current();
IntStream.range(0,random.nextInt(10,30)).mapToObj(i ->{
Map<String,Object> row=new LinkedHashMap<>();
row.put("sys_no","00000"+i);
row.put("org_name", "单位_" + i);
row.put("full_name", "姓名_" + i);
row.put("gender_desc", gender[random.nextInt(0, 2)]);
row.put("birth_date_str", TasDateTimeUtils.format(now, "yyyy-MM-dd"));
List<String> strings = Arrays.asList("2020-12-01", "2020-12-02");
String graduation_date_list_str = StringUtils.join(strings,"\n\r" );
System.out.println(graduation_date_list_str);
row.put("graduation_date_list_str", graduation_date_list_str);
row.put("graduation_school_list_str",StringUtils.join(Arrays.asList("烟台大学","南京大学"),"\n\r") );
return row;
}
).forEach(list::add);
model.addAttribute("dataList", list);
// file name
model.addAttribute("JXLS_FILENAME_KEY",
"title-registry-form-" + System.currentTimeMillis());
// generate view
return "report/title-registry-form-v1";
}
/**
* 作者: shanc
* 时间: 2021/4/19 10:55
* 描述: 自定义模板 jxls根据excel模板导出excel
*/
@GetMapping("/jxls-demo2")
public String jxlsDemo2(Model model) {
String [] gender={"男","女"};
Date now = new Date();
model.addAttribute("org_name", "中国中煤能源集团有限公司");
model.addAttribute("fill_form_user_name", "管理员");
List<Map<String,Object>> list =new ArrayList<>();
ThreadLocalRandom random =ThreadLocalRandom.current();
IntStream.range(0,random.nextInt(10,30)).mapToObj(i ->{
Map<String,Object> row=new LinkedHashMap<>();
row.put("sys_no","00000"+i);
row.put("org_name", "单位_" + i);
row.put("full_name", "姓名_" + i);
row.put("gender_desc", gender[random.nextInt(0, 2)]);
row.put("birth_date_str", TasDateTimeUtils.format(now, "yyyy-MM-dd"));
System.out.println(row);
return row;
}
).forEach(list::add);
model.addAttribute("dataList", list);
// file name
model.addAttribute("JXLS_FILENAME_KEY",
"jxls-demo2-" + System.currentTimeMillis());
// generate view
return "report/jxls-list3";
}
}
测试
通过docx4j 使html模板转word并导出
增加依赖
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-ImportXHTML</artifactId>
<version>8.0.0</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
<version>8.0.0</version>
</dependency>
工具类
package com.shan.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.output.StringBuilderWriter;
import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.springframework.stereotype.Component;
import org.thymeleaf.context.IContext;
import org.thymeleaf.spring5.SpringTemplateEngine;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.OutputStream;
/**
* Word doc generator.
*
* @author zhangbr
* @date 2020-12-31
*/
@Component
@Slf4j
public class InlineImageWordGenerator {
@Resource
private SpringTemplateEngine templateEngine;
/**
* generate doc and write to OutputStream
*
* @param templateName templateName
* @param ctx ctx, to set variables
* @param os OutputStream, e.g. {@code HttpServletResponse.getOutputStream()}
* @throws
*/
public void generate(String templateName, IContext ctx, OutputStream os) {
try (OutputStream os2 = os) {
// generate html string
StringBuilderWriter sbw2 = new StringBuilderWriter();
templateEngine.process(templateName, ctx, sbw2);
String htmlString = sbw2.toString();
if (log.isDebugEnabled()) {
log.debug("Template generated html:{}{}{}",
System.lineSeparator(),
htmlString,
System.lineSeparator());
}
// convert (x)html string to word doc
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
XHTMLImporterImpl xhtmlImporter = new XHTMLImporterImpl(wordMLPackage);
xhtmlImporter.setHyperlinkStyle("Hyperlink");
wordMLPackage.getMainDocumentPart().getContent()
.addAll(xhtmlImporter.convert(htmlString, null));
// write to output stream
wordMLPackage.save(os2);
} catch (Docx4JException | IOException e) {
throw new RuntimeException("error");
}
}
}
模板
thymeleafDemo2.html 模板
<html lang="zh_CN">
<head>
<meta charset="UTF-8"/>
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>Hello report.</title>
</head>
<body>
<h1 data-th-text="${hello}">Hello report.</h1>
<br/>
<table>
<thead>
<tr>
<th>id</th>
<th>信息</th>
<th>日期</th>
<th>图片</th>
</tr>
</thead>
<tbody>
<tr data-th-each="hel : ${hellos}">
<th data-th-text="${hel.id}">id</th>
<th data-th-text="${hel.hello}">message</th>
<th data-th-text="${#dates.format(hel.date, 'yyyy-MM-dd HH:mm')}">date</th>
<th>
<img src=""
data-th-src="${base64Image}"
data-th-title="'title_' + ${hel.hello}" title="title"
data-th-alt="'title_' + ${hel.hello}" alt="alt"
width="80px"
height="60px"/>
</th>
</tr>
</tbody>
</table>
</body>
</html>
测试
localhost:9001/thymeleafDemo/demo2
打开下载文件
reply-question.html模板
<html lang="zh_CN">
<head>
<meta charset="UTF-8"/>
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title data-th-text="${title}">论文答辩出题表</title>
</head>
<body data-th-each="dclrQuestion : ${dclrQuestionList}">
<div>
<p style="text-align:center;"
><span style="font-size:18pt; font-weight:bold"></span
></p>
<p style="text-align:center;" data-th-text="${title}">申报高级工程师系列人员论文答辩出题表</p>
<p style="text-align:center;"
><span style="font-size:18pt"> </span
></p>
<div>
<table border="1px" cellspacing="0" cellpadding="0"
style="border-collapse:collapse; width:524.1pt">
<tr>
<td>单位</td>
<td colspan="3" data-th-text="${dclrQuestion.orgName}">建安总部机关</td>
<td style="padding:0.62pt; vertical-align:middle; width:50.3pt">姓名</td>
<td colspan="2" data-th-text="${dclrQuestion.userName}">张三</td>
</tr>
<tr>
<td><p
><span> </span></p>论文<br/>题目<br/>内容<br/>摘要<p
><span> </span></p>
</td>
<td colspan="6" data-th-text="${dclrQuestion.paperTitleOrSummary}">选煤厂胶带输送机安装技术要点探讨</td>
</tr>
<tr>
<td rowspan="4">答<br/>辩<br/>题<br/>目</td>
<td>1</td>
<td colspan="5" data-th-text="${dclrQuestion.question1}">带式输送机胶带接头硫化工艺要点。</td>
</tr>
<tr>
<td>2</td>
<td colspan="5" data-th-text="${dclrQuestion.question2}">带式输送机在机体中跑偏,分析原因给出解决方案?</td>
</tr>
<tr>
<td>3</td>
<td colspan="5" data-th-text="${dclrQuestion.question3}">带式输送机驱动装置安装时二次灌浆工序如何进行?</td>
</tr>
<tr>
<td>4</td>
<td colspan="5" data-th-text="${dclrQuestion.question4}">机驱动装置安装时二</td>
</tr>
<tr style="border: none; height: 0px; ">
<td style="width: 85pt"></td>
<td style="width: 85pt"></td>
<td style="width: 85pt"></td>
<td style="width: 85pt"></td>
<td style="width: 85pt"></td>
<td style="width: 85pt"></td>
</tr>
</table>
</div>
<p>
<span> </span>
</p>
<p
><span style="font-family: 宋体; font-size: 14pt"
>出题专家:</span><span style="font-family: 宋体; font-size: 14pt" data-th-text="${dclrQuestion.expertName}"
>范强</span>
</p>
</div>
</body>
</html>
测试
localhost:9001/thymeleafDemo/reply-question-export
打开下载文件
使用jxls根据excel模板生成excel
看下篇文章