freemarker+itext5实现用模板方式,导出word和pdf(spring-boot直接可使用)

2023-11-12

第一步:需要引入的jar包

            <!--用于导出word文档-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-freemarker</artifactId>
            </dependency>

            <!-- 非spring-boot依赖 用于导出word文档-->
<!--            <dependency>-->
<!--                <groupId>org.freemarker</groupId>-->
<!--                <artifactId>freemarker</artifactId>-->
<!--                <version>2.3.28</version>-->
<!--            </dependency>-->
            <!-- 导出 pdf -->
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itextpdf</artifactId>
                <version>5.5.13</version>
            </dependency>
            <dependency>
                <groupId>com.itextpdf.tool</groupId>
                <artifactId>xmlworker</artifactId>
                <version>5.5.13</version>
            </dependency>
            <!--        解决Linux下导出pdf,不显示中文问题-->
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itext-asian</artifactId>
                <version>5.2.0</version>
            </dependency>

第二步:准备模板文件(当然,不使用模板在代码中拼也可以,但是有点麻烦)

模板实例文件:https://download.csdn.net/download/Copy_ing/13117006

2.1 word模板: 使用xml格式的模板文件

    说明:首先准备一个word文档,将word的内容编写为你最终要的样子.  然后另存为xml格式的文件,接着在根据自己的需要,将xml文件中要动态替换的关键字替换为${关键字} 的格式.  最后将文件后缀修改为ftl

2.2 pdf模板: 使用html格式的模板文件(对html的格式要求,比较严格,一定要仔细检查.)

    说明:首先也是创建一个word文件,编写成最终要导出的样子,然后另存为html格式的文件.然后同上.

模板文件放置位置:   

例:

第三步:(上代码)

package com.gs.hiring.app.util;

import com.gs.hiring.app.vo.customer.resume.CustomerResumeInfoVO;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontProvider;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author copying
 * @date 2020/11/5 10:40
 * 根据模板文件(.ftl)生成对应的word文档
 *
 * 使用说明: 官方文档:http://freemarker.foofun.cn/dgui_misc_userdefdir.html
 *     1.首先准备一份指定格式的word文档.
 *     2.将其中的参数 修改为指定格式: ${?}  (? 字段名)
 *     3.将word文档,另存为xml格式的文件.
 *     4.修改文件后缀为.ftl
 *
 */
@Slf4j
public class ResumeWordExportUtil {
    private ResumeWordExportUtil(){
        super();
    }
    private static ResumeWordExportUtil service = null;

    public static ResumeWordExportUtil getInstance() {
        if(service == null) {
            synchronized(ResumeWordExportUtil.class){
                if(service == null) {
                    service = new ResumeWordExportUtil();
                }
            }
        }
        return service;
    }

    /**
     * 生成要导出的文件
     * @param templateFilePath
     * @param data
     * @param response
     * @param isCh
     */
    public void createResumeFile(String templateFilePath, CustomerResumeInfoVO data,Integer type, HttpServletResponse response, boolean isCh) throws Exception {
        String fileName;
        if(isCh){
            fileName=data.getChName()+" 简历";
        }else {
            fileName=data.getEnName()+" resume";
        }

        String fileType="doc";
        if(Objects.nonNull(type)){
            if(type==1){
                fileType="pdf";
            }
        }
        setResponseHeader(response,fileName,fileType);
        OutputStream out=response.getOutputStream();
        Writer responseOut = new OutputStreamWriter(out);
        try{
            Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
            configuration.setClassForTemplateLoading(this.getClass(), "/templates");
            Template template = configuration.getTemplate(templateFilePath, "UTF-8");
            if(type==1){
                Writer writerPdf=new StringWriter();
                template.process(data, writerPdf);
                String pdf=writerPdf.toString();
                Document document=new Document();
                PdfWriter pdfWriter =PdfWriter.getInstance(document,out);
                FontProvider fontProvider=new ChinaFontProvide();
                document.open();
                XMLWorkerHelper.getInstance().parseXHtml(pdfWriter,document,new ByteArrayInputStream(pdf.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8,fontProvider);
                document.close();
                responseOut = new OutputStreamWriter(out);
            }else {
                //获取头像信息
                if (isGetStandardPhoto(data.getStandardPhoto())) {
                    data.setStandardPhoto(image2Base64(data.getStandardPhoto()));
                }
                template.process(data, responseOut);
            }
        }catch (Exception e){
            throw e;
        } finally {
            try {
                responseOut.flush();
                responseOut.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 提供中文
     * 不用字体文件,显示中文的解决方法
     */
    public static final class ChinaFontProvide implements FontProvider {

        @Override
        public Font getFont(String arg0, String arg1, boolean arg2, float arg3,
                            int arg4, BaseColor arg5) {
            BaseFont bfChinese = null;
            try {
                bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return new Font(bfChinese, 10, Font.NORMAL);
        }

        @Override
        public boolean isRegistered(String arg0) {
            return false;
        }
    }

    /**
     * 判断是否获取头像
     * @return boolean
     */
    private boolean isGetStandardPhoto(String url){
        if(Objects.nonNull(url) && !"".equals(url)){
            //校验url格式
            return url.startsWith("http://") || url.startsWith("https://");
        }
        return false;
    }

    /**
     * 通过url获取图片
     * @param imgUrl  图片地址
     * @return 图片编码
     */
    public String image2Base64(String imgUrl) {
        URL url;
        InputStream is = null;
        ByteArrayOutputStream outStream = null;
        HttpURLConnection httpUrl = null;
        try{
            url = new URL(imgUrl);
            httpUrl = (HttpURLConnection) url.openConnection();
            httpUrl.connect();
            httpUrl.getInputStream();
            is = httpUrl.getInputStream();
            outStream = new ByteArrayOutputStream();
            //创建一个Buffer字符串
            byte[] buffer = new byte[1024];
            //每次读取的字符串长度,如果为-1,代表全部读取完毕
            int len;
            //使用一个输入流从buffer里把数据读取出来
            while( (len=is.read(buffer)) != -1 ){
                //用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度
                outStream.write(buffer, 0, len);
            }
            // 对字节数组Base64编码
            return encodeImage(outStream.toByteArray());
        }catch (Exception e) {
            e.printStackTrace();
        }
        finally{
            if(is != null)
            {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outStream != null)
            {
                try {
                    outStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(httpUrl != null)
            {
                httpUrl.disconnect();
            }
        }
        return imgUrl;
    }

    /**
     * 图片转字符串
     * @param image 图片
     * @return 编码
     */
    public static String encodeImage(byte[] image){
        return replaceEnter(Base64.getEncoder().encodeToString(image));
    }

    public static String replaceEnter(String str){
        String reg ="[\n-\r]";
        Pattern p = Pattern.compile(reg);
        Matcher m = p.matcher(str);
        return m.replaceAll("");
    }

    private void setResponseHeader(HttpServletResponse response, String fileName,String fileType) {
        try {
            response.reset();
            // 设置生成的文件类型
            response.setContentType("application/ms"+fileType);
            // 设置文件头编码方式和文件名
            response.setCharacterEncoding("UTF-8");
            // 在浏览器中测试生效,postman中文件名为response,无法修改
            response.setHeader("Content-disposition", "attachment;filename="
                    .concat(String.valueOf(URLEncoder.encode(fileName + "."+fileType, "UTF-8"))));
            // 此设置,可保证web端可以取到文件名
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     *  把路径的\替换成/
     * @param path 原地址
     * @return 处理后的地址
     */
    private String pathReplace(String path) {
        while(path != null && path.contains("\\")) {
            path = path.replace("\\", "/");
        }
        return path;
    }
}

遇到的问题:

1.直接生成的html文件格式不严谨,导致itext无法解析.

   解决方案:1.html标签必须要,一一对应,有开始,有结束.例:<tr></tr>     2.只有一行的标签必须有结束符号.例:<br/>  <meta />

2.freemarker 动态赋值时,要进行属性检查 . 具体语法请参考官方文档: http://freemarker.foofun.cn/dgui_misc_userdefdir.html

3.linux环境,导出的pdf文件不显示中文(本地导出pdf显示中文,到服务器上就不显示了)    解决方案: 由于没有设置字体文件导致,在上面代码中已经解决.

4.导出word时,无法显示图片(freemarker不支持超链接).   解决方法:在代码中获取图片,转换为Base64位编码,直接放到xml中. 

注:要是有什么问题,或对你有所帮助,欢迎点赞\评论.

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

freemarker+itext5实现用模板方式,导出word和pdf(spring-boot直接可使用) 的相关文章

  • 插入最大日期(独立于数据库)

    在我的本地设置中 我使用一个简单的 H2 数据库 托管 解决方案将有另一个 类似但不相同 数据库 我需要将最大可能日期插入到日期时间列中 我尝试使用 Instant MAX 但是 这会导致列中出现 169104626 12 11 20 08
  • org.apache.sling.api.resource,version=[2.3,3) -- 无法解析

    您好 我无法访问我的项目内容 我已经上传了从 CQ 访问内容所需的所有包 我唯一能看到的是 org apache sling api resource version 2 3 3 无法解析 这是否是异常的原因 如果是 请告诉我如何解决 中Q
  • OSGi:如果不取消服务会发生什么

    这是我获取 OSGi 服务的方式 ServiceReference reference bundleContext getServiceReference Foo class getName Foo foo Foo bundleContex
  • Java AES 128 加密方式与 openssl 不同

    我们遇到了一种奇怪的情况 即我们在 Java 中使用的加密方法会向 openssl 生成不同的输出 尽管它们在配置上看起来相同 使用相同的键和 IV 文本 敏捷的棕色狐狸跳过了懒狗 加密为 Base64 字符串 openssl A8cMRI
  • java中如何连接字符串

    这是我的字符串连接代码 StringSecret java public class StringSecret public static void main String args String s new String abc s co
  • wait() 在游戏中如何工作?

    在 playframework 的文档中here http www playframework org documentation 1 2 1 asynchronous已写 public static void loopWithoutBlo
  • 按第一列排序二维数组,然后按第二列排序

    int arrs 1 100 11 22 1 11 2 12 Arrays sort arrs a b gt a 0 b 0 上面的数组已排序为 1 100 1 11 2 12 11 22 我希望它们按以下方式排序a 0 b 0 首先 如果
  • 我可以使用子接口重新编译公共 API 并保持二进制兼容性吗?

    我有一个公共 API 在多个项目中多次使用 public interface Process
  • 如何模拟从抽象类继承的受保护子类方法?

    如何使用 Mockito 或 PowerMock 模拟由子类实现但从抽象超类继承的受保护方法 换句话说 我想在模拟 doSomethingElse 的同时测试 doSomething 方法 抽象超类 public abstract clas
  • 匿名类上的 NotSerializedException

    我有一个用于过滤项目的界面 public interface KeyValFilter extends Serializable public static final long serialVersionUID 7069537470113
  • 在 Netbeans 8 上配置 JBoss EAP 的问题

    我已经下载了 JBoss EAP 7 并正在 Netbeans 8 上配置它 我已经到达向导 实例属性 其中要求从选择框中选择 域 当我打开选择框时 它是空的 没有什么可以选择的 因此 完成 按钮也处于非活动状态 这使得无法完成配置 我通过
  • 将 SignedHash 插入 PDF 中以进行外部签名过程 -workingSample

    遵循电子书第 4 3 3 节 PDF 文档的数字签名 https jira nuxeo com secure attachment 49931 digitalsignatures20130304 pdf 我正在尝试创建一个工作示例 其中 客
  • 很好地处理数据库约束错误

    再一次 它应该很简单 我的任务是在我们的应用程序的域对象中放置一个具有唯一约束的特定字段 这本身并不是一个很大的挑战 我刚刚做了以下事情 public class Location more fields Column unique tru
  • Javafx过滤表视图

    我正在尝试使用文本字段来过滤表视图 我想要一个文本字段 txtSearch 来搜索 nhs 号码 名字 姓氏 和 分类类别 我尝试过在线实施各种解决方案 但没有运气 我对这一切仍然很陌生 所以如果问得不好 我深表歉意 任何帮助将不胜感激 我
  • Struts 2 + Sitemesh 3 集成 - FreemarkerDecoratorServlet 中的 NPE

    我将 Struts 2 版本 2 3 14 3 与 Sitemesh 3 版本 3 0 alpha 2 一起使用 并且在某些情况下遇到 NullPointerException 首先 这是我的 web xml 中的 struts2 site
  • Netty:阻止调用以获取连接的服务器通道?

    呼吁ServerBootstrap bind 返回一个Channel但这不是在Connected状态 因此不能用于写入客户端 Netty 文档中的所有示例都显示写入Channel从它的ChannelHandler的事件如channelCon
  • Java中的Object类是什么?

    什么是或什么类型private Object obj Object http download oracle com javase 6 docs api java lang Object html是Java继承层次结构中每个类的最终祖先 从
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • FileOutputStream.close() 中的设备 ioctl 不合适

    我有一些代码可以使用以下命令将一些首选项保存到文件中FileOutputStream 这是我已经写了一千遍的标准代码 FileOutputStream out new FileOutputStream file try BufferedOu
  • ServletContainer 类未找到异常

    我无法再编译我的球衣项目 并且出现以下异常 GRAVE Servlet Project API threw load exception java lang ClassNotFoundException com sun jersey spi

随机推荐

  • python-OpenCV-人脸、眼睛,微笑检测

    文章目录 前言 一 人脸检测是什么 二 案例 总结 前言 随着人工智能的不断发展 OpenCV这门技术也越来越重要 很多人都开启了学习OpenCV 本文就介绍了OpenCV的基础内容 提示 以下是本篇文章正文内容 下面案例可供参考 案例 F
  • python中如何导入math模块_Python中的math模块如何进行数学运算?

    在数学之中 除了加减乘除四则运算之外 还有其它跟多的运算 比如乘方 开方 对数运算等等 其实我们的计算机如同我们人类一样也具备了基本的运算常识 知道了基本的运算规则与方法 但是计算机如何高速 精确的运算呢 在python中 基于基本的运算规
  • prometheus安装以及错误汇总

    首先 最简洁的安装就是通过 编译好的包 开箱即用 下面是链接 https prometheus io download d dhttps prometheus io download 但是为了后续更好的使用 建议下载源码 然后编译 编译过程
  • Scala集合和遍历

    目录 1 列表 1 1 定义 1 1 1 示例一 1 1 2 示例二 1 1 3 示例三 2 可变列表 2 1 定义 2 1 1 示例一 2 1 2 示例二 2 2 可变列表操作 2 2 1 示例 3 列表常用操作 3 1 判断列表是否为空
  • 毕业季:我和未来有个对话

    2013年迎来新中国建国以来人数最多的大学毕业生 699万 史上最难就业季 成为一个全社会关注的热点话题 由中央电视台综合频道与唯众传媒联合打造的关注青年人成长的首档青年电视公开课节目 开讲啦 邀请四位跨界嘉宾来开讲 并且和青年人一起和未来
  • jvm虚拟机_JVM虚拟机五连问,能过并不是只靠运气

    对于Java程序员来说 在众多Java知识点当中JVM是很重要的一块 每个Java程序都离不开Java虚拟机 Java程序的运行依靠具体的Java虚拟机实例 在Java虚拟机规范中 分别用子系统 内存区 数据类型以及指令这几个术语来描述的
  • 两个数值互换的几种方式

    一 建立临时变量 1 普通的方法 思路简介 建立一个临时变量 通过temp a a b b temp来实现交换 缺点 这只是一种假交换 由于这只是在函数内部临时变量间的交换 所以当函数退出 函数栈帧被释放 原本的值并没有交换 具体方法 in
  • MongoDB的使用

    MongoDB 是一个跨平台的 以 JSON 为数据模型的文档数据库 是当前 NoSQL 数据库产品中最热门的一种 它介于关系数据库和非关系数据库之间 是非关系数据库当中功能最丰富 最像关系数据库的产品 1 MongoDB 与 MySQL
  • 使用DVD光盘或者ISO作为本地YUM源

    Yum 全称为 Yellow dog Updater Modified 是一个在 Fedora RedHat CentOS 中的 Shell 前端软件包管理器 基于 RPM 包管理 能够从指定的服务器自动下载 RPM 包并且安装 可以自动处
  • 摸爬滚打DirectX11_day01——VS2015与DirectX11的配置

    什么是DirectX11 DirectX 微软提供的应用程序接口集 APIs 被设计为在运行 windows 操作系统平台上用来提供给开发人员控制硬件的底层接口 它的各个组件提供了访问不同硬件的能力 包括图形 显卡 声音 声卡 GPU 输入
  • 数据库原理(二 )

    文章目录 一 关系模型有关概念 二 关系的类型和性子 2 1 类型 2 2 基本关系的六条性质 三 E R图转换为关系模型的方法 3 1 实体联系 1 1 3 2 实体联系 1 n 方法一 方法 二 3 3 联系实体 m n 四 关系模型的
  • 官方AI语音系统电销机器人系统搭建

    端是VUE后端是java还有CC 4台服务器组成nginx kafka mysql数据库 fs 支持大并发 通话录音存储七牛云可以自定义录音存储时长不用担心录音多影响系统硬盘存储空间可自定义删除录音存留时间 市面上的都是单服务器然后加挂大数
  • 以太坊智能合约教程(一)搭建以太坊私有链搭建

    环境说明 win10 64位 geth1 6 5 1 安装geth 安装完成以后 先创建2个账号 geth account new 然后建立 mygenesis json nonce 0x0000000000000042 difficult
  • 更改SVN的用户名、密码等信息的两种方式

    一 背景 在刚入职一家公司的时候 经常会去安装一些版本管理工具 例如 git或者svn 下面就拿svn来举例 假如你拿到的电脑是上位同事留下的 里面的记录并没有被完全清空 当你安装好svn 大概率还是会使用上一位同事的配置文件 那在此之后你
  • Verilog HDL FPGA 从入门到放弃(1)

    这是一篇入门文章 笔者也曾经迷茫过 也很困惑过 硬件编程是怎么样的 但是功夫不负有心人 希望我的文章获得读者的认同 谦虚使人进步 希望不足之处请提意见 对于有意思的东西大家可以探讨一下 硬件编程verilog 建模 一个简单的模型 流水灯的
  • 使用Spring Boot和EasyExcel的导入导出

    在当今信息化社会 数据的导入和导出在各种业务场景中变得越来越重要 为了满足复杂的导入导出需求 结合Java编程语言 Spring Boot框架以及EasyExcel库 我们可以轻松地构建出强大而灵活的数据处理系统 本文将引导您通过一个案例学
  • Odoo(OpenERP)补货规则笔记整理 - 草稿

    感谢Jeffery Jason ccdos roson 等人分享讨论 推式规则 移库规则 从一个库位推送到另一个库位 拉式规则 补货规则 包括生产 采购 移动 进行仓库设置时 会自动创建默认的拉式规则 最小库存规则 再订货规则 补货 某个库
  • iphone 服务器未响应,iPhone 死机没反应?先试试这些方法!

    iPhone 突然死机了 相信很多果粉都遇过这种情况 通常在手机死机的时候 无论我们怎么点屏幕都没反应 如果在一些紧急情况下发生死机 是很头疼的一件事 那么 iPhone 死机没反应 我们到底应该怎么处理呢 今天 疯师傅教你们三招 自己在家
  • 静态类型

    从程序运行或者二进制的角度 或者说 运行时 的角度来看 静态类型就是 没有类型 因为程序实际上不在运行时保存任何的静态类型 这也是静态类型又被称为 编译时类型 的原因 因为一旦经过编译 静态类型系统就基本上消失殆尽了 为什么又说基本上了呢
  • freemarker+itext5实现用模板方式,导出word和pdf(spring-boot直接可使用)

    第一步 需要引入的jar包