全文介绍
poi-tl(poi template language)是Word模板引擎,使用Word模板和数据创建很棒的Word文档 。
常用标签介绍(官网):
1文本:{{var}} 2. 图片:{{@var}}
3表格:{{#var}} 4. 列表:{{*var}}
5区块对:{{?sections}}{{/sections}} 6. 嵌套:{{+var}}
本次制作的Demo,简写代码、多注释,只为容易理解,读者可根据自己需求进行重构代码、优化代码。
1根据{{table}}、[content],实现word表格导出(有模板)
2根据{{?sections}}{{/sections}},多个表格段落进行表格、图片插入
环境配置
<!-- poi-tl --><dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.0</version>
</dependency>
<!-- 导出图片指定html格式 -->
<dependency>
<groupId>io.github.draco1023</groupId>
<artifactId>poi-tl-ext</artifactId>
<version>0.4.2</version>
</dependency>
1根据{{table}}、[content],实现word表格导出(有模板)
1.1模板
模板在代码中位置
1.2代码实现
//测试类实现
public class ExportWordTest7 { @Test
void exportWord() throws IOException {
//1 获取模板文件流
InputStream resourceAsStream = this.getClass().getResourceAsStream("/templates/pmwordBg.docx");
//2 word数据集合
Map<String, Object> params = new HashMap<>();
params.put("workContentss",getDynamicFlag1());
//3 图片指定插件
ConfigureBuilder builder = Configure.builder();
Configure config = builder.build();
HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy();
config.customPolicy("workContentss", policy);
//4 word导出
//本地测试
String filePath = FileUtil.getProjectPath() + "document" + File.separator + "牧羊人导出实例.docx";
dynamicExport(params,new File(filePath),resourceAsStream,config,null,false);
//浏览器文件名称设置
// String fileName = wordExportPmProtermVo.getProjectName()+"第"+wordExportPmProtermVo.getJournalTerm()+"期" + ".docx";
}
/**
* @param data 填充数据
* @param filePath 临时路径
* @param resource 模板路径
* @param response 通过浏览器下载需要
* @param isBrowser true-通过浏览器下载 false-下载到临时路径
*/
public void dynamicExport(Map<String, Object> data, File filePath, InputStream resource, Configure config, HttpServletResponse response, boolean isBrowser){
try {
if (Boolean.FALSE.equals(isBrowser)) {//本地
File parentFile = filePath.getParentFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
FileOutputStream out = new FileOutputStream(filePath);
XWPFTemplate.compile(resource, config).render(data).writeAndClose(out);
} else {//浏览器
ServletOutputStream out = response.getOutputStream();
XWPFTemplate.compile(resource, config).render(data).writeAndClose(out);
}
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 一、本日主要工作数据整理
* @Author: syq
* @Date: 2023/4/6 18:31
*/
public List<Map<String,Object>> getDynamicFlag1(){
List<Map<String, Object>> dynamicFlag = new ArrayList<>();
for (int i = 0; i < 4; i++) {
Map<String, Object> dynamicTableMap = new HashMap<>();
dynamicTableMap.put("xh", i+1);//序号
dynamicTableMap.put("workContent", "工作内容");//专业主要工作
dynamicTableMap.put("professional", "专业");//专业
dynamicTableMap.put("inspectedBy", "检查人");//检查人
dynamicFlag.add(dynamicTableMap);
}
return dynamicFlag;
}
}
/** * @author: muyangren
* @Date: 2023/1/14
* @Description: com.muyangren.utils
* @Version: 1.0
*/
public class FileUtil {
/**
* 创建FileItem
* @param file
* @param fieldName
* @return
*/
public static MultipartFile createFileItem(File file, String fieldName) {
FileItemFactory factory = new DiskFileItemFactory(16, null);
FileItem item = factory.createItem(fieldName, ContentType.MULTIPART_FORM_DATA.toString(), true, file.getName());
int bytesRead = 0;
byte[] buffer = new byte[8192];
try {
FileInputStream fis = new FileInputStream(file);
OutputStream os = item.getOutputStream();
while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
return new CommonsMultipartFile(item);
}
/**
* 下载到本地路径
* @param file
* @return
* @throws IOException
*/
public static File fileDownloadToLocalPath(MultipartFile file) {
File destFile = null;
try {
//获取文件名称
if (StringUtils.isEmpty(file.getOriginalFilename())){
throw new ServerException("导入模板失败!");
}
String fileName = file.getOriginalFilename();
//获取文件后缀
String pref = fileName.lastIndexOf(".") != -1 ? fileName.substring(fileName.lastIndexOf(".") + 1) : null;
//临时文件
//临时文件名避免重复
String uuidFile = UUID.randomUUID().toString().replace("-", "") + "." + pref;
destFile = new File(FileUtil.getProjectPath() + uuidFile);
if (!destFile.getParentFile().exists()) {
destFile.getParentFile().mkdirs();
}
file.transferTo(destFile);
} catch (IOException e) {
e.printStackTrace();
}
return destFile;
}
/**
* @return 文件路径
*/
public static String getProjectPath(){
String os = System.getProperty("os.name").toLowerCase();
//windows下
if (os.indexOf("windows")>=0) {
return "C://temp/";
}else{
return "/usr/local/temp/";
}
}
1.3效果图
2根据{{?sections}}{{/sections}},多个表格段落进行表格、图片插入
2.1模板
2.2代码实现
package com.muyangren;import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.muyangren.enums.StyleEnum;
import com.muyangren.utils.FileUtil;
import com.muyangren.utils.HtmlUtil;
import com.muyangren.vo.Attachment;
import com.muyangren.vo.Attachments;
import org.apache.commons.lang3.StringUtils;
import org.ddr.poi.html.HtmlRenderPolicy;
import org.junit.jupiter.api.Test;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
/**
* @Author: syq
* @Description: ${description}
* @Date: 2023/4/9 14:33
* @Version: 1.0
*/
public class ExportWordTest8 {
/**
* @Description: word按照指定模式进行导出
* 1 获取模板文件流
* 2 数据与word锚点匹配
* 3 图片指定插件
* 4 word导出
* @Author: syq
* @Date: 2023/4/6 17:22
*/
@Test
void exportWord() throws IOException {
//1 获取模板文件流
InputStream resourceAsStream = this.getClass().getResourceAsStream("/templates/pmwordtest.docx");
//2 word数据集合
Map<String, Object> params = new HashMap<>();
//2.3 数据与word锚点匹配
params.put("dynamicFlag1",getDynamicFlag1());
params.put("workTitle","工作概述");
//3 图片指定插件
HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy();
ConfigureBuilder builder = Configure.builder();
Configure config = builder.build();
config.customPolicy("pictureUrl", htmlRenderPolicy);
//4 word导出
//本地测试
String filePath = FileUtil.getProjectPath() + "document" + File.separator + "牧羊人导出实例.docx";
dynamicExport(params,new File(filePath),resourceAsStream,config,null,false);
//浏览器文件名称设置
// String fileName = wordExportPmProtermVo.getProjectName()+"第"+wordExportPmProtermVo.getJournalTerm()+"期" + ".docx";
// try {
// response.setHeader("Content-disposition", "attachment;filename=".concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
// } catch (UnsupportedEncodingException e) {
// e.printStackTrace();
// }
}
/**
* @param data 填充数据
* @param filePath 临时路径
* @param resource 模板路径
* @param response 通过浏览器下载需要
* @param isBrowser true-通过浏览器下载 false-下载到临时路径
*/
public void dynamicExport(Map<String, Object> data, File filePath, InputStream resource, Configure config, HttpServletResponse response, boolean isBrowser){
try {
if (Boolean.FALSE.equals(isBrowser)) {//本地
File parentFile = filePath.getParentFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
FileOutputStream out = new FileOutputStream(filePath);
XWPFTemplate.compile(resource, config).render(data).writeAndClose(out);
} else {//浏览器
ServletOutputStream out = response.getOutputStream();
XWPFTemplate.compile(resource, config).render(data).writeAndClose(out);
}
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 一、本日主要工作数据整理
* @Author: syq
* @Date: 2023/4/6 18:31
*/
public List<Map<String,Object>> getDynamicFlag1() throws IOException {
List<Map<String, Object>> dynamicFlag = new ArrayList<>();
for (int i = 0; i < 4; i++) {
Map<String, Object> dynamicTableMap = new HashMap<>();
dynamicTableMap.put("xh", i+1);//序号
dynamicTableMap.put("workContent", "工作内容");//专业主要工作
dynamicTableMap.put("professional", "专业");//专业
dynamicTableMap.put("inspectedBy", "检查人");//检查人
//url方式
// List<String> list = new ArrayList<>();
// list.add("https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%9B%BE%E7%89%87&hs=0&pn=0&spn=0&di=7207123747399008257&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&ie=utf-8&oe=utf-8&cl=2&lm=-1&cs=1204793430%2C3263400171&os=3423103612%2C2074051226&simid=1204793430%2C3263400171&adpicid=0&lpn=0&ln=30&fr=ala&fm=&sme=&cg=&bdtype=0&oriquery=%E5%9B%BE%E7%89%87&objurl=https%3A%2F%2Fup.deskcity.org%2Fpic_source%2F2f%2Ff4%2F42%2F2ff442798331f6cc6005098766304e39.jpg&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3B1jfhvtpy_z%26e3B562AzdH3F15ogs5w1AzdH3Fdd9m0m-a-axa_z%26e3Bip4s&gsm=&islist=&querylist=&dyTabStr=MCwxLDYsMyw0LDUsMiw3LDgsOQ%3D%3D");
// dynamicTableMap.put("pictureUrl",dealWithPictureWidthAndHeight(getBase64String(list)));
//本地方式
File file = new File("C:/aa.png");
String baseString = base64String(file);
baseString = "data:image/jpeg;base64," + baseString;
String base = "<img src=" + baseString + " style=\"width:194.1pt;height:126.05pt;\"/>" ;
// "<img src=" + baseString + " style=\"width:194.1pt;height:126.05pt;\"/>"+
// "<img src=" + baseString + " style=\"width:194.1pt;height:126.05pt;\"/>";
// String base = "<p style=\"white-space:pre-wrap;\"><img src=" + baseString + " style=\"width:194.1pt;height:126.05pt;\"/>";
dynamicTableMap.put("pictureUrl", dealWithPictureWidthAndHeight(base));//一张或多张图片
dynamicFlag.add(dynamicTableMap);
}
return dynamicFlag;
}
public String base64String(File file) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
FileInputStream fileInputStream = new FileInputStream(file);
byte[] bytes = new byte[1024];
//用来定义一个准备接收图片总长度的局部变量
int len;
//将流的内容读取到bytes中
while ((len = fileInputStream.read(bytes)) > 0) {
//将bytes内存中的内容从0开始到总长度输出出去
out.write(bytes, 0, len);
}
//通过util包中的Base64类对字节数组进行base64编码
return Base64.getEncoder().encodeToString(out.toByteArray());
}
/**
* @Description: 根据url获取图片base64
* @Author: syq
* @Date: 2023/4/7 16:37
*/
public String getBase64String(List<String> listPath){
String base64String = "";
for (int i = 0; i < listPath.size(); i++) {
String address = listPath.get(i);
if(StringUtils.isNotBlank(address)){
String get = netSourceToBase64(address, "GET");
if(StringUtils.isNotBlank(get)){
get = "data:image/jpeg;base64," + get;
base64String = base64String + "<img src=" + get + " style=\"width:194.1pt;height:126.05pt;\"/>";
}
}
}
return base64String;
}
public static String netSourceToBase64(String srcUrl, String requestMethod) {
ByteArrayOutputStream outPut = new ByteArrayOutputStream();
byte[] data = new byte[1024 * 8];
try {
// 创建URL
URL url = new URL(srcUrl);
// 创建链接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(requestMethod);
conn.setConnectTimeout(10 * 1000);
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
//连接失败/链接失效/文件不存在
return null;
}
InputStream inStream = conn.getInputStream();
int len = -1;
while (-1 != (len = inStream.read(data))) {
outPut.write(data, 0, len);
}
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
// 对字节数组Base64编码
return Base64.getEncoder().encodeToString(outPut.toByteArray());
}
/**
* @Description: 图片处理
* @Author: syq
* @Date: 2023/4/7 16:04
*/
private String dealWithPictureWidthAndHeight(String content) {
List<HashMap<String, String>> imagesFiles = HtmlUtil.regexMatchWidthAndHeight(content);
if (imagesFiles.size() > 0) {
for (HashMap<String, String> imagesFile : imagesFiles) {
String newFileUrl = imagesFile.get(StyleEnum.NEW_FILE_URL.getValue());
String fileUrl = imagesFile.get(StyleEnum.FILE_URL.getValue());
if (newFileUrl != null) {
content = content.replace(fileUrl, newFileUrl);
}
}
}
return content;
}
}