1.介绍easypoi
EasyPoi是一款基于POI的Java快速导出/导入Excel工具。它在POI的基础上进行了封装,提供了更加简洁易用的API,使得生成Excel文件更加容易和高效。
使用EasyPoi可以轻松地生成Excel文件,并支持多种格式,如xlsx、xls、csv等。同时,EasyPoi也支持读取Excel文件,可以方便地获取其中的数据,并进行相应的处理。
EasyPoi具有以下特点:
-
简单易用:EasyPoi提供了简洁易用的API,使用起来非常方便。
-
支持多种格式:EasyPoi支持多种格式的Excel文件,如xlsx、xls、csv等。
-
灵活性高:EasyPoi支持多种数据格式,包括文本、数字、日期等,同时也支持复杂数据结构,如嵌套表格等。
-
导入导出高效:EasyPoi在性能上进行了优化,导入导出速度快,使用起来非常高效。
总之,EasyPoi是一个非常实用的Java导入导出Excel工具,它可以帮助开发者轻松地生成和处理Excel文件。
easypoi网站:悟耘科技 – Powered by MinDoc
2.改善的word工具类
在百度上面搜索了很多类似的文章,因为自己的业务需求,然后将其他博主的工具类进行了完善,最后还会说一些我做这个工具类 遇到的一些坑,
2.1导入相关依赖
如果是需要 jar包的话可以在下面给我留言
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.3.0</version>
</dependency>
<!--pdf-->
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-export-fo</artifactId>
<version>6.1.0</version>
<!--因为项目中slf4j版本冲突,忽悠这个依赖中的slf4j版本-->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
2.2准备好word模板放在resources项目下
我自己放在这个目录下,因为项目打包的原因,规范一点
2.3word模板样式
里面包含了最简单的一些参数注入
如果需要打印遍历集合,模板可以参考其他博主:java使用easypoi导出word文档,包含图片,表格,文字;_easypoi导出word list图片_一点博客的博客-CSDN博客
大概是这个样子,比较简单(其他 博主 的 模板)
模板指令:
2.4.工具类
这个工具类可以直接拿去用,我把这个工具 类封装成一个bean,需要调用的时候在业务层注入
import cn.afterturn.easypoi.word.WordExportUtil;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.docx4j.Docx4J;
import org.docx4j.convert.out.FOSettings;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
/**
* @ClassName WordUtil
* @Description 描述:easypoi导出工具类
* @Author xuan
* @Date 2023/8/22
* @Version 2.0
**/
@Component
public class WordUtil {
@Value(value = "${jeecg.path.upload}")
private String uploadpath; //从yml文件读取文件下载路径
@Autowired
private ResourceLoader resourceLoader;
public String exportPdf(WordTemplateEnum templateType, Map<String, Object> params) throws IOException {
String temPath = templateType.getPath();
String absolutePath = resourceLoader.getResource("classpath:" + temPath).getFile().getAbsolutePath();
//String absolutePath = ResourceUtils.getFile("classpath:static\\template\\1.docx").getAbsolutePath();
return exportPdf(absolutePath,params);
}
/**
* 导出PDF
*
* @param templatePath word模板地址
* @param params 替换的参数
* @return pdf的完全路径
*/
public String exportPdf(String templatePath, Map<String, Object> params) {
// 生成的wold文档文件名
String woldFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".docx";
//保存的文件路径名
String saveDir = uploadpath + File.separator + "pdf";
// 生成的pdf文件名
String pdfFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".pdf";
// 导出wold文档 返回值为生成的wold文档全路径
String word = exportWord(templatePath, saveDir, woldFileName, params);
cn.hutool.core.lang.Assert.notNull(word, "word路径不能为空");
// 自定义生成的pdf全路径
String pdfPath = saveDir + File.separator + pdfFileName;
// 导出pdf,同时删除生成的wold文档
convertDocx2Pdf(word, pdfPath);
return pdfPath;
}
/**
* 导出word
* 模版变量中变量格式:{{foo}}
*
* @param templatePath word模板地址
* @param saveDir word文档保存的路径
* @param fileName 文件名
* @param params 替换的参数
*/
public String exportWord(String templatePath, String saveDir, String fileName, Map<String, Object> params) {
Assert.notNull(templatePath, "模板路径不能为空");
Assert.notNull(saveDir, "临时文件路径不能为空");
Assert.notNull(fileName, "导出文件名不能为空");
Assert.isTrue(fileName.endsWith(".docx"), "word导出请使用docx格式");
if (!saveDir.endsWith("/")) {
saveDir = saveDir + File.separator;
}
File dir = new File(saveDir);
if (!dir.exists()) {
dir.mkdirs();
}
String savePath = saveDir + fileName;
try {
XWPFDocument doc = WordExportUtil.exportWord07(templatePath, params);
FileOutputStream fos = new FileOutputStream(savePath);
doc.write(fos);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
return savePath;
}
public String exportWord(String templatePath, String temDir, String fileName, Map<String, Object> params, HttpServletRequest request, HttpServletResponse response) {
Assert.notNull(templatePath,"模板路径不能为空");
Assert.notNull(temDir,"临时文件路径不能为空");
Assert.notNull(fileName,"导出文件名不能为空");
Assert.isTrue(fileName.endsWith(".docx"),"word导出请使用docx格式");
if (!temDir.endsWith("/")){
temDir = temDir + File.separator;
}
File dir = new File(temDir);
if (!dir.exists()) {
dir.mkdirs();
}
try {
String userAgent = request.getHeader("user-agent").toLowerCase();
if (userAgent.contains("msie") || userAgent.contains("like gecko")) {
fileName = URLEncoder.encode(fileName, "UTF-8");
} else {
fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
}
XWPFDocument doc = WordExportUtil.exportWord07(templatePath, params);
String tmpPath = temDir + fileName;
FileOutputStream fos = new FileOutputStream(tmpPath);
doc.write(fos);
// 设置强制下载不打开
response.setContentType("application/force-download");
// 设置文件名
response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
OutputStream out = response.getOutputStream();
doc.write(out);
out.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
//这一步看具体需求,要不要删
//delFileWord(temDir,fileName);
}
return temDir + fileName;
}
/**
* 删除零时生成的文件
*/
public void delFileWord(String filePath, String fileName) {
File file = new File(filePath + fileName);
File file1 = new File(filePath);
file.delete();
file1.delete();
}
/**
* @param wordPath word文件路径
* @param pdfPath pdf输出路径
*/
public void convertDocx2Pdf(String wordPath, String pdfPath) {
OutputStream os = null;
InputStream is = null;
if (pdfPath.endsWith("/")) {
pdfPath = pdfPath + File.separator;
}
try {
is = new FileInputStream(new File(wordPath));
WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(is);
Mapper fontMapper = new IdentityPlusMapper();
fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));
fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
mlPackage.setFontMapper(fontMapper);
os = new java.io.FileOutputStream(pdfPath);
//docx4j docx转pdf
FOSettings foSettings = Docx4J.createFOSettings();
foSettings.setWmlPackage(mlPackage);
Docx4J.toFO(foSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL);
is.close();//关闭输入流
os.close();//关闭输出流
} catch (Exception e) {
e.printStackTrace();
try {
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
} finally {
// 删除word文档的地址
File file = new File(wordPath);
if (file != null && file.isFile() && file.exists()) {
file.delete();
}
}
}
/**
* @param path 图片路径
* @return
* @throws IOException
*/
public byte[] getImageBase64(String path) throws IOException {
InputStream input = new FileInputStream(path);
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int numBytesRead = 0;
while ((numBytesRead = input.read(buf)) != -1) {
output.write(buf, 0, numBytesRead);
}
byte[] data = output.toByteArray();
output.close();
input.close();
return data;
}
}
其中exportpdf方法调用,第一个参数是模板路径,第二参数是 注入的参数,返回是导出的pdf模板完全路径(业务 需求)
2.5调用工具类
我就简单的在测试类中写一下,肯定是没有问题的,
import cn.afterturn.easypoi.entity.ImageEntity;
@Autowired
private WordUtil wordUtil;
@Test
public void testPostDoctor() throws IOException {
Map<String, Object> params = new HashMap<>();
params.put("enterpriseName","张三");
params.put("contactsName","李四");
params.put("contactsPhone","12345678");
ImageEntity image = new ImageEntity();
image.setHeight(50);
image.setWidth(50);
image.setUrl("https://lmg.jj20.com/up/allimg/4k/s/02/2109250006343S5-0-lp.jpg");
params.put("testCode", image);
String exportPdf = wordUtil.exportPdf("static\\template\\1.docx", params);
System.out.println("exportPdf = " + exportPdf);
}
下面的在application.yml文件写的文件下载路径
jeecg :
path :
#文件上传根目录 设置
upload: D://opt//upFiles
这个根据自己的需求来写,因为我考虑 到项目打包发服务器的,我想想应该没有其他的了把
3.1坑
自己遇到了很多坑在导出模板的,我是先将 wold模板导为wold文件,然后在将wold文件转为pdf
第一个坑:因为自己的模板放在resources目录下面,项目完整启动会将模板加载进target目录里面,会 默认的对docx文件进行压缩,一压缩就乱码, 我找了半个小时才找出问题
大概是这个样子,我们需要在pom.xml文件指明不压缩docx文件
<!-- 避免font文件的二进制文件格式压缩破坏 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>woff</nonFilteredFileExtension>
<nonFilteredFileExtension>woff2</nonFilteredFileExtension>
<nonFilteredFileExtension>eot</nonFilteredFileExtension>
<nonFilteredFileExtension>ttf</nonFilteredFileExtension>
<nonFilteredFileExtension>svg</nonFilteredFileExtension>
<nonFilteredFileExtension>docx</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
这样就不会压缩一些 文件
第二个坑:
当一些属性你没有在map找到的时候,运行 会报空指针
解决方案:
在map中写满,如果没有值,可以写空字符串都可以,不能不写,
还有一种方案,将工具类底层调用的poiUtil中重写,运行的时候就会先调用 重写的这个类
然后将代码的这部分修改
private static Boolean isTrue(String[] keys, Map<String, Object> map) throws Exception {
if (keys.length == 1) {
String constant = null;
if ((constant = isConstant(keys[0])) != null) {
return Boolean.valueOf(constant);
}
Object paramsValue = PoiPublicUtil.getParamsValue(keys[0], map);
if (Objects.isNull(paramsValue)){
return false;
}
return Boolean.valueOf(paramsValue.toString());
}
if (keys.length == 3) {
Object first = evalNoParse(keys[0], map);
Object second = evalNoParse(keys[2], map);
return PoiFunctionUtil.isTrue(first, keys[1], second);
}
throw new ExcelExportException("判断参数不对");
}
这样即使忘记在map中写了,也不会报错
目前就这些问题了,什么时候遇到新问题了在补充一下,有什么不懂的,或者有问题的可以留言
明天周末,周末愉快!!!!
文章出处登录后可见!