vue +SpringBoot + FreeMarker + FlyingSaucer 实现PDF在线预览、打印、下载

在此,首先感谢

这篇链接的博主

我借助此文完成了我的需求,下载pdf简历 ,所以在此记录一笔

项目的大概功能如下 vue的页面点击下载按钮就能下载简历   ,谷歌浏览器

 浏览器中点开这个简历如下这样的,  里面还有打印和下载

 一 首先你需要引入如下  pom依赖

   <!-- freemarker依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

        <!-- FlyingSaucer依赖 -->
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf</artifactId>
            <version>9.1.12</version>
        </dependency>

二   工具类

接着     工具类 : 实在强大 ,可根据自身业务修改

package com.ruoyi.common.util;


import com.lowagie.text.pdf.BaseFont;
import com.platform.domain.ProcessJobresumeinfo;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
/**
 * 功能:pdf处理工具类
 *
 */
public class PdfUtils {
    private PdfUtils() {
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(PdfUtils.class);

    /**
     * 按模板和参数生成html字符串,再转换为flying-saucer识别的Document
     *
     * @param templateName freemarker模板名称
     * @param variables    freemarker模板参数
     * @return Document
     */
    private static Document generateDoc(FreeMarkerConfigurer configurer, String templateName, ProcessJobresumeinfo variables)  {
        Template tp;
        try {
            tp = configurer.getConfiguration().getTemplate(templateName);
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
            return null;
        }

        StringWriter stringWriter = new StringWriter();
        try(BufferedWriter writer = new BufferedWriter(stringWriter)) {
            try {
                tp.process(variables, writer);
                writer.flush();
            } catch (TemplateException e) {
                LOGGER.error("模板不存在或者路径错误", e);
            } catch (IOException e) {
                LOGGER.error("IO异常", e);
            }
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            return builder.parse(new ByteArrayInputStream(stringWriter.toString().getBytes()));
        }catch (Exception e){
            LOGGER.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 核心: 根据freemarker模板生成pdf文档
     *
     * @param configurer   freemarker配置
     * @param templateName freemarker模板名称
     * @param out          输出流
     * @param info     freemarker模板参数
     * @throws Exception 模板无法找到、模板语法错误、IO异常
     */
    private static void generateAll(FreeMarkerConfigurer configurer, String templateName, OutputStream out,ProcessJobresumeinfo  info) throws Exception {
        if (info==null) {
            LOGGER.warn("警告:简历模板参数为空!");
            return;
        }

        ITextRenderer renderer = new ITextRenderer();
        Document doc = generateDoc(configurer, templateName, info);
        renderer.setDocument(doc, null);
        //设置字符集(宋体),此处必须与模板中的<body style="font-family: SimSun">一致,区分大小写,不能写成汉字"宋体"
        ITextFontResolver fontResolver = renderer.getFontResolver();
        fontResolver.addFont("simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        //展现和输出pdf
        renderer.layout();
        renderer.createPDF(out, false);
        renderer.finishPDF(); //完成pdf写入
    }

    /**
     * pdf下载
     *
     * @param configurer   freemarker配置
     * @param templateName freemarker模板名称(带后缀.ftl)
     * @param info     模板参数集
     * @param response     HttpServletResponse
     * @param fileName     下载文件名称(带文件扩展名后缀)
     */
    public static void download(FreeMarkerConfigurer configurer, String templateName, ProcessJobresumeinfo info, HttpServletResponse response, String fileName) {
        // 设置编码、文件ContentType类型、文件头、下载文件名
        response.setCharacterEncoding("utf-8");
        response.setContentType("multipart/form-data");
        try {
            response.setHeader("Content-Disposition", "attachment;fileName=" +
                    new String(fileName.getBytes("gb2312"), "ISO8859-1"));
        } catch (UnsupportedEncodingException e) {
            LOGGER.error(e.getMessage(), e);
        }
        try (ServletOutputStream out = response.getOutputStream()) {
            generateAll(configurer, templateName, out, info);
            out.flush();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    /**
     * pdf预览
     *
     * @param configurer   freemarker配置
     * @param templateName freemarker模板名称(带后缀.ftl)
     * @param info     模板参数集
     * @param response     HttpServletResponse
     */
    public static void preview(FreeMarkerConfigurer configurer, String templateName, ProcessJobresumeinfo info, HttpServletResponse response) {
        try (ServletOutputStream out = response.getOutputStream()) {
            generateAll(configurer, templateName, out, info);
            out.flush();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }
}

注意   这里有三个东西,resources下的templates加入pdfResume.ftl模板  (自己加) ,  还有一个simsun.ttc字体,这个说是window系统有,但是我系统并没有,可以百度搜索下载资源,这里不做分享

flt模板

<!DOCTYPE html>
<html>
<head lang="en">
    <title>${name}简历信息</title>
    <style>
        @page {
            size: 210mm 297mm; /*设置纸张大小:A4(210mm 297mm)、A3(297mm 420mm) 横向则反过来*/
            margin: 0.25in;
            padding: 1em;
            @bottom-center {
                content: "xxx 版权所有";
                font-family: SimSun;
                font-size: 12px;
                color: red;
            };
            @top-center {
                content: element(header)
            };
            @bottom-right {
                content: "第" counter(page) "页  共 " counter(pages) "页";
                font-family: SimSun;
                font-size: 12px;
                color: #000;
            };
        }
    </style>
</head>
<body style="font-family: 'SimSun'">
<h2>个人简历</h2><br/>
<div>
    <label>姓名: ${name}</label><br/>
</div>
<div>
    <label>性别:
        <#if sex==0>
            未知
        </#if>
        <#if sex==1>
            男
        </#if>
        <#if sex==2>
            女
        </#if>
    </label><br/>
</div>
<#if birthday??>
    <div>
        <label>出生年月: ${birthday?string("yyyy-MM-dd")}</label>
    </div>
</#if>
<#if phone??>
    <div>
        <label>联系电话: ${phone} </label>
    </div>
</#if>
<#if weixin??>
    <div>
        <label>微信号: ${weixin}</label>
    </div>
</#if>
<#if advantage??>
    <div>
        <label>个人优势: ${advantage}</label>
    </div><br/>
</#if>

<h3>求职期望</h3>

<#if hopes?exists>
    <#list hopes as hope>
        <h4>求职期望 ${hope_index+1}</h4>
        <#if hope.jobtype??>
            <div>
                <label>求职类型: ${hope.jobtype}</label>
            </div>
        </#if>

        <#if hope.jobcity??>
            <div>
                <label>工作城市: ${hope.jobcity}</label>
            </div>
        </#if>
        <#if hope.jobtype??>
            <div>
                <label>期望职位: ${hope.jobtype}</label>
            </div>
        </#if>

        <#if hope.job??>
            <div>
                <label>期望行业: ${hope.job}</label>
            </div>
        </#if>
        <div>
            <label>薪资要求:
                <#if hope.paymin??>
                    ${hope.paymin}
                </#if>
                <#if hope.paymax??>
                    ${hope.paymax}
                </#if>
            </label>
        </div><br/><br/>
    </#list>
</#if>
<h3>工作经历</h3>
<#if workExpers?exists>
    <#list workExpers as we>
        <h4>工作经历 ${we_index+1}</h4>
        <#if we.cname??>
            <div>
                <label>公司名称: ${we.cname}</label>
            </div>
        </#if>

        <#if we.business??>
            <div>
                <label>所属行业: ${we.business}</label>
            </div>
        </#if>
        <#if we.addtime??>
            <div>
                <label>入职时间: ${we.addtime}</label>
            </div>
        </#if>

        <#if we.endtime??>
            <div>
                <label>离职时间: ${we.endtime}</label>
            </div>
        </#if>
        <#if we.jobtitle??>
            <div>
                <label>职位名称: ${we.jobtitle}</label>
            </div>
        </#if>
        <#if we.department??>
            <div>
                <label>所属部门: ${we.department}</label>
            </div>
        </#if>
        <#if we.jobcontent??>
            <div>
                <label>工作内容: ${we.jobcontent}</label>
            </div>
        </#if>
        <br/>
    </#list>
</#if>

<h3>项目经验</h3>
<#if projectExps?exists>
    <#list projectExps as pj>
        <h4>项目 ${pj_index+1}</h4>
        <#if pj.projectname??>
            <div>
                <label>项目名称: ${pj.projectname}</label>
            </div>
        </#if>
        <#if pj.role??>
            <div>
                <label>担任角色: ${pj.role}</label>
            </div>
        </#if>
        <#if pj.projectstarttime??>
            <div>
                <label>项目开始时间: ${pj.projectstarttime}</label>
            </div>
        </#if>
        <#if pj.projectendtime??>
            <div>
                <label>项目结束时间: ${pj.projectendtime}</label>
            </div>
        </#if>
        <#if pj.projectinfo??>
            <div>
                <label>项目链接: ${pj.projectinfo}</label>
            </div>
        </#if>
        <br/>
    </#list>
</#if>

<h3>教育经历</h3>
<#if educations?exists>
    <#list educations as ed>
        <h4>教育经历 ${ed_index+1}</h4>
        <#if ed.schoolname??>
            <div>
                <label>学校名称: ${ed.schoolname}</label>
            </div>
        </#if>
        <#if ed.education??>
            <div>
                <label>学历: ${ed.education}</label>
            </div>
        </#if>
        <#if ed.profession??>
            <div>
                <label>专业: ${ed.profession}</label>
            </div>
        </#if>
        <#if ed.estart??>
            <div>
                <label>时间段:
                    ${ed.estart} -- ${ed.eend}</label>
            </div>
        </#if>
        <#if ed.schoolexperience??>
            <div>
                <label>在校经历:
                    ${ed.schoolexperience} </label>
            </div>
        </#if>
        <br/>
    </#list>
</#if>

<#if certificate??>
    <div>
        <label>资格证书:
            ${certificate} </label>
    </div>
</#if>

</body>
</html>

三   web框架自行引入

controller    info是简历信息 ,查询数据库那一些操作这里不分享了

   
  /**
     * pdf预览
     *
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     */
    @RequestMapping(value = "/preview", method = RequestMethod.GET)
    public void preview(HttpServletRequest request, HttpServletResponse response) {
        String id = request.getParameter("id");
        Jobresumeinfo info = infoService.selectinfoById(id);
        PdfUtils.preview(configurer,"pdfResume.ftl",info,response);
    }



 /**
     * pdf下载
     *
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     */
    @RequestMapping(value = "/download", method = RequestMethod.GET)
    public void download(HttpServletRequest request, HttpServletResponse response) {
        String id = request.getParameter("id");
        Jobresumeinfo info = infoService.selectinfoById(id);
        PdfUtils.download(configurer,"pdfResume.ftl",info,response,"简历信息");
    }

vue部分

html


      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button size="mini"    type="text"  @click="downPDF(scope.row.user)">下载</el-button>
          <el-button
            size="mini"
            type="text"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['platform:jobResumeInfo:edit']"
          >查看</el-button>
          <el-button  v-if="scope.row.status == '3' && scope.row.status != '4'"
            size="mini"
            type="text"
            @click="editWorkHours1(scope.row)"
            v-hasPermi="['platform:jobResumeInfo:edit']"
          >填工时</el-button>

          <el-button v-if="scope.row.status != '2' && scope.row.status != '3' && scope.row.status != '4' "
            size="mini"
            type="text"
            @click="refuseSubmitForm1(scope.row)"
            v-hasPermi="['platform:jobResumeInfo:edit']"
          >婉拒</el-button>

          <el-button v-if="scope.row.status != '3' && scope.row.status != '4'  "
            size="mini"
            type="text"
            @click="passSubmitForm1(scope.row)"

          >通过</el-button>


          <!--<el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['platform:jobResumeInfo:remove']"
          >删除</el-button> -->
        </template>
      </el-table-column>

js

 /** 下载pdf简历 **/
    downPDF(row){
   
      var pdfname = row.name + "-"+row.phone;
      downPDF(row).then(response => {
         console.log(this.pdfUrl)
        const elink = document.createElement("a");

        elink.href = window.URL.createObjectURL(new Blob([response], {type: `application/pdf`}));
        elink.style.display = 'none';
        elink.setAttribute('download', pdfname);
        document.body.appendChild(elink);
        elink.click();
        URL.revokeObjectURL(elink.href); // 释放URL 对象
        document.body.removeChild(elink);

      });

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值