JAVA之利用easypoi将word模板导出为pdf(可带图片)

1.介绍easypoi

EasyPoi是一款基于POI的Java快速导出/导入Excel工具。它在POI的基础上进行了封装,提供了更加简洁易用的API,使得生成Excel文件更加容易和高效。

使用EasyPoi可以轻松地生成Excel文件,并支持多种格式,如xlsx、xls、csv等。同时,EasyPoi也支持读取Excel文件,可以方便地获取其中的数据,并进行相应的处理。

EasyPoi具有以下特点:

  1. 简单易用:EasyPoi提供了简洁易用的API,使用起来非常方便。

  2. 支持多种格式:EasyPoi支持多种格式的Excel文件,如xlsx、xls、csv等。

  3. 灵活性高:EasyPoi支持多种数据格式,包括文本、数字、日期等,同时也支持复杂数据结构,如嵌套表格等。

  4. 导入导出高效: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中写了,也不会报错

目前就这些问题了,什么时候遇到新问题了在补充一下,有什么不懂的,或者有问题的可以留言

明天周末,周末愉快!!!!

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2023年12月8日
下一篇 2023年12月8日

相关推荐