springboot简单使用poi-tl

发布时间 2023-09-08 19:52:46作者: 快乐小洋人

简介

poi-tl是一个基于Apache POI的开源Word模板引擎,比 Freemarker 的功能更加强大。

官方文档地址:http://deepoove.com/poi-tl/

导包

导入包时,依赖说明参考官方文档,导入包不适配可能会造成一些问题,此处可以使用

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.3</version>
        </dependency>
        <dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.12.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.3</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.5.6</version>
        </dependency>

使用

所有的标签都是以{{开头,以}}结尾:{{title}},其余标签参考官方文档

区块对由前后两个标签组成,开始标签以?标识,结束标签以/标识:{{?sections}}{{/sections}}

区块对开始和结束标签中间可以包含多个图片、表格、段落、列表、图表等,开始和结束标签可以跨多个段落,也可以在同一个段落,但是如果在表格中使用区块对,开始和结束标签必须在同一个单元格内,因为跨多个单元格的渲染行为是未知的。
区块对在处理一系列文档元素的时候非常有用,位于区块对中的文档元素可以被渲染零次,一次或N次,这取决于区块对的取值。

  • False或空集合:隐藏区块中的所有文档元素
  • 非False且不是集合:显示区块中的文档元素,渲染一次
  • 非空集合:根据集合的大小,循环渲染区块中的文档元素

数据类似于哈希或者字典,可以是Map结构(key是标签名称):

map:

Map<String, Object> data = new HashMap<>();
data.put("name", "Sayi");
data.put("start_time", "2019-08-04");

entity:

public class Data {
  private String name;
  private String startTime;
  private Author author;
}

以流的方式进行输出:

template.write(OutputStream stream);
// 文件流:
template.write(new FileOutputStream("output.docx"));
// 网络流:
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment;filename=\""+"out_template.docx"+"\"");

// HttpServletResponse response
OutputStream out = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(out);
template.write(bos);
bos.flush();
out.flush();
PoitlIOUtils.closeQuietlyMulti(template, bos, out);

表格

创建:

实现:

List<Users> list = new ArrayList<>(0);
data.put("users", list);
ConfigureBuilder builder = Configure.builder();
LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
builder.bind("users", policy);
XWPFTemplate template = XWPFTemplate.compile(file.getPath(), builder.build()).render(data);

附件

可以直接在文档内打开,文档内保存了附件内容不是映射的本地地址

List<List<Objet>> users = new ArrayList<>(0);
for (int i = 0; i < 4; i++) {
	List<Object> list = new ArrayList<>(0);
	list.add("zhangsan");
	list.add("zhangsan123");
	list.add("pwd123");
	list.add("2022-12-01 12:00:00");
}
List<Object> titles = Arrays.asList("用户名", "账号", "密码", "创建时间");
// 写入缓存文件
String url = "C:\\Users\\Public\\Documents\\users.xlsx";
File file = new File(url);
if (file.isFile()) {
    file.delete();
}
ExcelWriter writer = ExcelUtil.getWriter(file);
writer.writeHeadRow(titles).write(users);
writer.flush().close();
removeUrls.add(url);
// 创建插件
AttachmentRenderData attach = Attachments.ofLocal(url, AttachmentType.XLSX).create();
data.put("users", attach);
AttachmentRenderPolicy attachmentRenderPolicy = new AttachmentRenderPolicy();
ConfigureBuilder builder = Configure.builder();
builder.bind("users", attachmentRenderPolicy);
// 获取模板文件
File fileTemplate = ResourceUtils.getFile("src/main/resources/templates/template.docx");
if (fileTemplate.exists()) {
	XWPFTemplate template = XWPFTemplate.compile(fileTemplate.getPath(), builder.build()).render(data);
	response.setContentType("application/octet-stream");
	response.setHeader("Content-disposition", "attachment;filename=\"" + "out_template.docx" + "\"");

	// HttpServletResponse response
	OutputStream out = response.getOutputStream();
	BufferedOutputStream bos = new BufferedOutputStream(out);
	template.write(bos);
	bos.flush();
	out.flush();
	PoitlIOUtils.closeQuietlyMulti(template, bos, out);
}
// 删除缓存的附件
if (file.isFile()) {
	file.delete();
}

完整代码

private final String linuxUrl = "/home/";
private final String windowsUrl = "C:\\Users\\Public\\Documents\\";

/**
 * 导出根据word模板生成
 */
@Override
public void exportDoc(HttpServletResponse response) throws IOException {
	String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
	Map<String, Object> data = new HashMap<>(0);
	data.put("time", time);
	// 用户数据列表
	List<User> users = new ArrayList<>(0);
	for (int i = 0; i < 12; i++) {
		User user = new User();
		user.setUsername("zhangsan"+i);
		user.setAccount("zhangsan"+i);
		user.setPassword("12345"+i);
		user.setCreateTime("2022-12-01 12:00:00");
		users.add(user);
	}
	data.put("users", users);
	if (users > 10) {
		// 生成excel附件
		List<List<Object>> usersExcels = new ArrayList<>(0);
		for (int i = 0; i < users.size(); i++) {
			List<Object> list = new ArrayList<>(0);
			list.add(users.get(i).getUsername());
			list.add(users.get(i).getAccount());
			list.add(users.get(i).getPassword());
			list.add(users.get(i).getCreateTime());
			usersExcels.add(list);
		}
		List<Object> titles = Arrays.asList("用户名", "账号", "密码", "创建时间");
		String url = this.downloadFileLocal("users.xlsx", titles, usersExcels);
		removeUrls.add(url);
		AttachmentRenderData attach = Attachments.ofLocal(url, AttachmentType.XLSX).create();
		// 此时word文档内标签为{{usersExcels}}
		data.put("usersExcels", attach);
	} 
	File file = ResourceUtils.getFile("src/main/resources/templates/template.docx");
	if (file.exists()) {
		LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
		AttachmentRenderPolicy attachmentRenderPolicy = new AttachmentRenderPolicy();
		ConfigureBuilder builder = Configure.builder();
		builder.bind("users", policy);
		builder.bind("usersExcels", attachmentRenderPolicy);

		XWPFTemplate template = XWPFTemplate.compile(file.getPath(), builder.build()).render(data);
		response.setContentType("application/octet-stream");
		response.setHeader("Content-disposition", "attachment;filename=\"" + "out_template.docx" + "\"");

		// HttpServletResponse response
		OutputStream out = response.getOutputStream();
		BufferedOutputStream bos = new BufferedOutputStream(out);
		template.write(bos);
		bos.flush();
		out.flush();
		PoitlIOUtils.closeQuietlyMulti(template, bos, out);
		// 删除缓存的附件
		this.deleteCacheFiles(removeUrls);
	}
}

/**
 * 生成excel附件
 *
 * @param fileName 文件名
 * @param titles   表头
 * @param data     数据
 */
private String downloadFileLocal(String fileName, List<Object> titles, List<List<Object>> data) throws IOException {
	String url = "";
	String osName = System.getProperty("os.name").toLowerCase();
	if (Constants.isSystem(osName)) {
		// Windows
		url = windowsUrl + fileName;
	} else {
		// Linux
		url = linuxUrl + fileName;
	}
	File file = new File(url);
	if (file.isFile()) {
		file.delete();
	}
	ExcelWriter writer = ExcelUtil.getWriter(file);
	writer.writeHeadRow(titles).write(data);
	writer.flush().close();
	return url;
}

/**
 * 删除缓存附件
 */
private boolean deleteCacheFiles(List<String> urls) {
	for (String url : urls) {
		File files = new File(url);
		if (files.isFile()) {
			files.delete();
		}
	}
	return true;
}