赞
踩
首先奉上poi-tl官方文档地址:http://deepoove.com/poi-tl/#_%E7%89%88%E6%9C%AC
1.导包
gradle:
//有了poi-tl的依赖也需要poi的相关依赖,用以下版本可以避免或多或少的版本冲突问题
//少了poi的依赖可能报错java.lang.NoSuchMethodError: org.apache.poi.util.IOUtils.toByteArray(Ljava/io/InputStream;II)[B""
implementation 'com.deepoove:poi-tl:1.10.5'
implementation "org.apache.poi:poi:4.1.2"
<--一些poi的依赖-->
implementation group: 'org.apache.poi', name: 'ooxml-schemas', version: '1.4'
implementation "org.apache.poi:poi-ooxml:4.1.2"
<--aspose的依赖包-->
文件资源:https://download.csdn.net/download/m0_49605579/59212077 (不需要C币,免费,有说文件资源下载需要密码的,你做个任务白嫖就好了呀)
注:注意poi-tl的版本,不同版本之间对应的poi版本不一样,api也不一样,所以需要参考文档时要选择对应的版本文档,不然就会报一系列的异常。
配置结构
2.创建模板
模板样式任意(如下图),只需填充占位符即可,循环图片也可以使用{{?list}}{{@#this}} {{/list}}
代码少写个Map
3.编写破解pdf水印方法
package com.yswl.upms.util;
import com.aspose.words.Document;
import com.aspose.words.License;
import com.aspose.words.SaveFormat;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @Author 955
* @Date 2022-03-08 16:07
* @Description 导入导出工具类
*/
public class Word2PdfAsposeUtil {
public static boolean getLicense(InputStream inputStream) {
boolean result = false;
InputStream is = null;
try {
is=inputStream;
License aposeLic = new License();
aposeLic.setLicense(is);
result = true;
} catch (Exception e) {
e.printStackTrace();
}finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
public static boolean doc2pdf(String inPath, String outPath,InputStream inputStream) {
if (!getLicense(inputStream)) { // 验证License 若不验证则转化出的pdf文档会有水印产生
return false;
}
FileOutputStream os = null;
try {
long old = System.currentTimeMillis();
File file = new File(outPath); // 新建一个空白pdf文档
os = new FileOutputStream(file);
Document doc = new Document(inPath); // Address是将要被转化的word文档
// doc.set
doc.save(os, SaveFormat.PDF);// 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF,
// EPUB, XPS, SWF 相互转换
long now = System.currentTimeMillis();
System.out.println("pdf转换成功,共耗时:" + ((now - old) / 1000.0) + "秒"); // 转化用时
} catch (Exception e) {
e.printStackTrace();
return false;
}finally {
if (os != null) {
try {
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return true;
}
public static void main(String[] arg){
// String docPath = "D:\\report\\word\\交通态势日报-2021-01-10.docx";
// String pdfPath = "D:\\report\\word\\交通态势日报-2021-01-10.pdf";
// Word2PdfAsposeUtil.doc2pdf(docPath,pdfPath);
// UUID randomUUID = UUID.randomUUID();
// String replaceAll = randomUUID.toString().replaceAll("-", "");
// System.out.println("======> "+replaceAll);
long timeMillis = System.currentTimeMillis();
System.out.println("===> "+timeMillis);
}
}
4.编写导出工具方法(可选导出pdf/docx)
import com.aspose.words.Document;
import com.aspose.words.SaveFormat;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.util.PoitlIOUtils;
import com.wlj.util.Word2PdfAsposeUtil;
import lombok.AllArgsConstructor;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 导出
* @param response
* @param fileName 模板名
* @param map 数据map
* @param type 导出类型
* @param config 配置插件策略
*/
public static void exportFile(
HttpServletResponse response,
String fileName,
Map<String, Object> map,
String type,
Configure config) {
try {
// 获取Word模板,模板存放路径在项目的resources目录下
//Kit就是方法所在的类
InputStream ins = UserInfoServiceImpl.class.getResourceAsStream("/template/" + fileName);
//文件在上方导包资源内
InputStream xmlIn = UserInfoServiceImpl.class.getResourceAsStream("/license/license.xml");
// 破解spiredoc水印
Word2PdfAsposeUtil.getLicense(xmlIn);
//若没有策略需求调用compile(InputStream)的重载方法即可
XWPFTemplate template = XWPFTemplate.compile(ins, config).render(map);
// 浏览器端下载
response.setCharacterEncoding("utf-8");
if (WORD.equals(type)) {
response.setHeader("Content-disposition", "attachment;filename=\"" + fileName + "\"");
response.setContentType("application/msword");
OutputStream out = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(out);
template.write(bos);
bos.flush();
out.flush();
PoitlIOUtils.closeQuietlyMulti(template, bos, out);
} else if (PDF.equals(type)) {
response.setHeader(
"Content-Disposition",
"attachment;filename=".concat(java.lang.String.valueOf(URLEncoder.encode("导出实例.pdf", "UTF-8"))));
OutputStream out = response.getOutputStream();
response.setContentType("application/pdf");
//获取相对路径url
URL url = UserInfoServiceImpl.class.getResource("/template/");
template.writeAndClose(new FileOutputStream(url.getPath() + "temporary.docx"));
InputStream in = UserInfoServiceImpl.class.getResourceAsStream("/template/temporary.docx");
Document document = new Document(in);
document.save(out, SaveFormat.PDF);
IOUtils.copy(in, out);
out.flush();
PoitlIOUtils.closeQuietlyMulti(template, ins, in, out);
new File(url.getPath() + "temporary.docx").delete();
}
} catch (Exception e) {
e.printStackTrace();
}
}
封装占位符数据
Map<String, Object> resultMap = new HashMap<>();
//纯文本占位符
put("title", "Hi, poi-tl Word模板引擎");
//图片
// 指定图片路径
put("image", "/ce/logo.png");
// 设置图片宽高
put("image1", Pictures.ofLocal("logo.png").size(120, 120).create());
// 图片流
put("streamImg", Pictures.ofStream(new FileInputStream("logo.jpeg"), PictureType.JPEG)
.size(100, 120).create());
// 网络图片(注意网络耗时对系统可能的性能影响)
put("urlImg", Pictures.ofUrl("http://deepoove.com/images/icecream.png")
.size(100, 100).create());
// svg图片
put("svg", "https://img.shields.io/badge/jdk-1.6%2B-orange.svg");
// java图片
put("buffered", Pictures.ofBufferedImage(bufferImage, PictureType.PNG)
.size(100, 100).create());
//如果是循环(区块对标签{{?xxx}}{{/xxx}})
//要循环的集合
List<Map<String, Object>> list = new ArrayList();
for (int i = 0; i < 3; i++) {
//循环添加要循环的数据
Map<String, Object> map = new HashMap<>();
map.put("code", Pictures.ofBufferedImage(bufferImage, PictureType.PNG)
.size(100, 100).create());
list.add(map);
}
put("list", list);
导出表格(无表格模板使用table占位符)
编写直接输出表格数据方法
List<Map<String, Object>> l = new ArrayList<>();
List<对象> gxTypeModelVoList = 对象;
for (int i = 0; i < gxTypeModelVoList.size(); i++) {
对象 vo = 对象内集合;
Map<String, Object> m = new HashMap();
m.put("title", (i + 1) + "." + vo.getType());
RowRenderData row0 = Rows.of("序号", "内容", "步骤", "√")
.textColor("000000")
.bgColor("FFFFFF")
.center()
.create();
TableRenderData tableRenderData = new TableRenderData();
tableRenderData.addRow(row0);
TableStyle ttt = new TableStyle();
ttt.setColWidths(new int[] {700, 1300, 5500, 300});
// 设置表格宽度
ttt.setWidth("8000");
// 设置表格居中对齐
ttt.setAlign(TableRowAlign.LEFT);
tableRenderData.setTableStyle(ttt);
List<GxVo> gxListVo = gxTypeModelVoList.get(i).getGxListVo();
//封装循环行数据,与上面表头对应,是什么类型就添加对应对象,表格行就展示什么类型(图片、文本...)
RowRenderData ffffff = Rows.of(
j + 1 + "",
对象.getName(),
//替换换行
对象.getTechnology().replaceAll("\\\\n", "\n").replaceAll("<br/>", "\n"),
str)
.textColor("000000")
.bgColor("FFFFFF")
.textFontSize(8)
.create();
tableRenderData.addRow(ffffff);
}
}
}
List<RowRenderData> rows = tableRenderData.getRows();
MergeCellRule.MergeCellRuleBuilder builder = MergeCellRule.builder();
// 初始化合并起始行和结束行
int cell0start = 0;
int cell1start = 0;
int cell0end = 0;
int cell1end = 0;
// 每一行
for (int row = 0; row < rows.size(); row++) {
// 最后一行跨过去
if (row == rows.size() - 1) {
continue;
}
// 合并规则
RowRenderData rowRenderData0 = rows.get(row);
RowRenderData rowRenderData1 = rows.get(row + 1);
String row0Cell0 = rowRenderData0.getCells().get(0).getParagraphs().get(0).getContents().toString();
String row0Cell1 = rowRenderData0.getCells().get(1).getParagraphs().get(0).getContents().toString();
String row1Cell0 = rowRenderData1.getCells().get(0).getParagraphs().get(0).getContents().toString();
String row1Cell1 = rowRenderData1.getCells().get(1).getParagraphs().get(0).getContents().toString();
// 相等递加,直到不相等时合并
if (row0Cell0.equals(row1Cell0)) {
cell0end++;
} else {
Integer newStart = cell0start;
Integer newEnd = cell0end;
cell0start = row + 1;
cell0end = row + 1;
if (!newStart.equals(newEnd)) {
builder.map(MergeCellRule.Grid.of(newStart, 0), MergeCellRule.Grid.of(newEnd, 0));
}
}
if (row0Cell1.equals(row1Cell1)) {
cell1end++;
} else {
Integer newStart = cell1start;
Integer newEnd = cell1end;
cell1start = row + 1;
cell1end = row + 1;
if (!newStart.equals(newEnd)) {
builder.map(MergeCellRule.Grid.of(newStart, 1), MergeCellRule.Grid.of(newEnd, 1));
}
}
}
MergeCellRule rule = builder.build();
tableRenderData.setMergeRule(rule);
m.put("table", tableRenderData);
l.add(m);
}
resultMap.put("sections", l);
表格行循环插件
public void exportUserInfo(HttpServletResponse response,String type) {
List<UserInfo> list = list(wrapper);
List<Map<String, Object>> r = new ArrayList<>();
for (SysBaseUserInfo userInfo : list) {
Map<String, Object> m = new HashMap<>();
m.put("username", userInfo.getUsername());
m.put("name", userInfo.getName());
m.put("code", userInfo.getCode());
m.put("phone", userInfo.getPhone());
String avatar = userInfo.getAvatar();
try {
String[] split = avatar.split(",");
List<PictureRenderData> imgList = new ArrayList<>();
for (String s : split) {
InputStream in= 根据文件名获取文件流;
imgList.add(Pictures.ofStream(in, PictureType.JPEG)
.size(50, 50).create());
}
m.put("imgs", imgList);
} catch (Exception e) {
}
r.add(m);
}
Map<String, Object> rm = new HashMap<>();
rm.put("list", r);
//使用行循环插件
LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
//绑定插件所属名为list
Configure config = Configure.builder()
.bind("list", policy).build();
exportFile(response, "userInfo.docx", rm, type, config);
}
也可在行循环中插入区块对标签实现循环该列集合,如上循环图片(只需有List集合即可,map键名此处为"imgs")
注:行循环需要用到LoopRowTableRenderPolicy插件,数据循环标签放于表头左上,循环行标签使用[占位符名]循环(注意行数据类型,如图片为[@标签名],也可省略标签名[@#this]),这里有个小问题就是如果循环图片行列表会换行展示,现行方法只能把图片尺寸调小,官方说在同一段落就不会换行,这个暂未研究。
与方法一相同,封装数据集合结构为:
导出方法与上方相同
注:其实这些方法都从官方文档中能找到,怎么使用在业务需要,这里举一个项目经验例子以便以后学习使用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。