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

第一步:需要引入的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中. 

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

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值