freemarker生成word文件并导出

根据前端选择的表格数据生成word表格文档。要对若依字典数据进行处理,使导出的是字典标签(如:合格、不合格),而不是字典值(0,1)。同时实现下载好生成的word文件后,自动删除存储的生成文件,避免冗余。


一、环境准备

(1)JDK1.8
(2)SpringBoot2.3.7.RELEASE
(3)Freemarker 2.3.28

主要是导入freemarker

 <!-- freemarker-->
            <dependency>
                <groupId>org.freemarker</groupId>
                <artifactId>freemarker</artifactId>
                <version>2.3.28</version>
            </dependency>

二、编写代码

1.实体类

在下图位置创建一个实体类,内容是需要导出的数据和对应的get,set方法。同时在其中做好对字典数据的处理。
在这里插入图片描述代码如下:

package com.ruoyi.system.domain.TempEntity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.system.domain.DataApparatus;
import com.ruoyi.system.domain.vo.DataApparatusCheckSchemeVo;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;



public class TempDataApparatusCheckScheme extends BaseEntity {

    /** 检/校参量 */
    @Excel(name = "检/校参量")
    private String checkParameter;

    /** 检/校范围 */
    @Excel(name = "检/校范围")
    private String checkRange;

    /** 检/校依据 */
    @Excel(name = "检/校依据")
    private String checkBasis;

    /** 检定单位 */
    @Excel(name = "检定单位")
    private String checkUnit;

    /** 备注 */
    @Excel(name = "备注")
    private String rmk;

    /** 检/校结果 */
    @Excel(name = "检/校结果")
    private String checkResult;

    /** 仪器设备名称 */
    @Excel(name = "仪器设备名称")
    private String apparatusName;

    /** 实验室编号(字典) */
    @Excel(name = "实验室编号(字典)")
    private String labId;

    /** 仪器设备型号 */
    @Excel(name = "仪器设备型号")
    private String apparatusModel;

    /** 出厂编号 */
    @Excel(name = "出厂编号")
    private String factoryNum;


    /** 使用部门(字典) */
    @Excel(name = "使用部门(字典)")
    private String userDepartment;

    /** 检/校周期 */
    @Excel(name = "检/校周期")
    private String checkCycle;


    public String getCheckParameter() {
        return checkParameter;
    }

    public void setCheckParameter(String checkParameter) {
        this.checkParameter = checkParameter;
    }

    public String getCheckRange() {
        return checkRange;
    }

    public void setCheckRange(String checkRange) {
        this.checkRange = checkRange;
    }

    public String getCheckBasis() {
        return checkBasis;
    }

    public void setCheckBasis(String checkBasis) {
        this.checkBasis = checkBasis;
    }

    public String getCheckUnit() {
        return checkUnit;
    }

    public void setCheckUnit(String checkUnit) {
        this.checkUnit = checkUnit;
    }

    public String getRmk() {
        return rmk;
    }

    public void setRmk(String rmk) {
        this.rmk = rmk;
    }

    public String getCheckResult() {
        return checkResult;
    }

    public void setCheckResult(String checkResult) {
        this.checkResult = checkResult;
    }

    public String getApparatusName() {
        return apparatusName;
    }

    public void setApparatusName(String apparatusName) {
        this.apparatusName = apparatusName;
    }

    public String getLabId() {
        return labId;
    }

    public void setLabId(String labId) {
        this.labId = labId;
    }

    public String getApparatusModel() {
        return apparatusModel;
    }

    public void setApparatusModel(String apparatusModel) {
        this.apparatusModel = apparatusModel;
    }

    public String getFactoryNum() {
        return factoryNum;
    }

    public void setFactoryNum(String factoryNum) {
        this.factoryNum = factoryNum;
    }

    public String getUserDepartment() {
        return userDepartment;
    }

    public void setUserDepartment(String userDepartment) {
        this.userDepartment = userDepartment;
    }

    public String getCheckCycle() {
        return checkCycle;
    }

    public void setCheckCycle(String checkCycle) {
        this.checkCycle = checkCycle;
    }

    public TempDataApparatusCheckScheme(DataApparatusCheckSchemeVo s, HashMap<String, ArrayList<SysDictData>> dictmap){
        this.labId = s.getLabId();
        this.apparatusName = s.getApparatusName();
        this.apparatusModel = s.getApparatusModel();
        this.factoryNum = s.getFactoryNum();
        this.userDepartment = s.getUserDepartment();
        this.checkCycle = s.getCheckCycle();
        this.checkParameter = s.getCheckParameter();
        this.checkRange = s.getCheckRange();
        this.checkBasis = s.getCheckBasis();
        this.checkUnit = s.getCheckUnit();
        this.rmk = s.getRmk();
        //check result
        for( SysDictData dictData:dictmap.get("check_result")){
            if(dictData.getDictValue().equals(s.getCheckResult())){
                this.checkResult =dictData.getDictLabel();
                break;
            }
        }
    }

    public String toString() {
        return "TempDataApparatusCheckScheme{" +
                "labId=" + labId +
                ", apparatusName='" + apparatusName + '\'' +
                ", apparatusModel='" + apparatusModel + '\'' +
                ", factoryNum='" + factoryNum + '\'' +
                ", userDepartment='" + userDepartment + '\'' +
                ", checkCycle='" + checkCycle + '\'' +
                ", checkParameter='" + checkParameter + '\'' +
                ", checkRange='" + checkRange + '\'' +
                ", checkBasis='" + checkBasis + '\'' +
                ", checkUnit='" + checkUnit + '\'' +
                ", checkResult='" + checkResult + '\'' +
                ", rmk='" + rmk + '\'' +
                '}';
    }
}

2.mapper.xml文件

修改mapper.xml文件。增加根据前端传来的数组查询数据的方法。

 <!--    根据ID数组 查询所有的检校信息 -->
    <select id="selectDataApparatusCheckSchemeByIds" parameterType="Long" resultMap="DataApparatusCheckSchemeVoResult">
        <include refid="selectDataApparatusCheckSchemeVo" />
        where data_apparatus_check_scheme.id in
        <foreach item="id" collection="array" open="(" separator="," close=")">
            #{id}
        </foreach>
    </select>

3.mapper.java文件

 /**
     * 根据ID数组查询仪器设备校准方案列表
     * @param ids 仪器设备校准方案主键集合
     * @return 仪器设备校准方案列表集合
     */

    public List<DataApparatusCheckSchemeVo> selectDataApparatusCheckSchemeByIds(Long[] ids);

4.Service.java

  /**
     * 根据ID数组查询仪器校准方案列表
     * @param ids 仪器设备检校方案主键集合
     * @return 仪器设备校准方案集合
     */
    public List<DataApparatusCheckSchemeVo> selectDataApparatusCheckSchemeByIds(Long[] ids);

5.ServiceImpl.java

 /**
     * 根据ID数组查询仪器设备校准方案列表
     * @param ids
     * @return
     */
    @Override
    public List<DataApparatusCheckSchemeVo> selectDataApparatusCheckSchemeByIds(Long[] ids) {
        return dataApparatusCheckSchemeMapper.selectDataApparatusCheckSchemeByIds(ids);
    }

6.wordUtil工具类(重点)

在下图所示位置建立wordUtil工具类,用于生成word文档以及其他相关操作
在这里插入图片描述
代码如下:

package com.ruoyi.common.utils.file;

import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.DateUtils;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.Version;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Map;

/**
 * @ClassName WordUtil
 * @Description 使用Freemarker生成Word文档工具类
 * @Author ruoyi
 * @Date 2022/6/15 10:06
 **/


public class WordUtil {
    /**
     * 使用Freemarker自动生成Word文档
     * @param dataMap   保存Word文档中所需要的数据
     * @param templatePath  模板文件的绝对路径
     * @param templateFile  模板文件的名称
     * @param generateFile  生成文件的路径+名称
     * @throws Exception
     */

/** 原版生成word方法 */
    public static void generateWord(Map<String, Object> dataMap, String templatePath,String templateFile, String generateFile) {
        // 设置FreeMarker的版本
        Configuration configuration = new Configuration(new Version("2.3.28"));
        // 设置Freemarker的编码格式
        configuration.setDefaultEncoding("UTF-8");
        Writer out = null;
        try{
            // 设置FreeMarker生成Word文档所需要的模板的路径
            configuration.setDirectoryForTemplateLoading(new File(templatePath));
            // 设置FreeMarker生成Word文档所需要的模板名称
            Template t = configuration.getTemplate(templateFile, "UTF-8");
            // 创建一个Word文档的输出流
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(generateFile)), "UTF-8"));
            //FreeMarker使用Word模板和数据生成Word文档
            t.process(dataMap, out);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (out != null) {
            try {
                out.flush();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /** 新的生成word方法 */
    public static void CreateWord(HttpServletResponse resp, HttpServletRequest req,Map<String, Object> dataMap, String templatePath,String templateFile, String generateFile) {
        // 设置FreeMarker的版本
        Configuration configuration = new Configuration(new Version("2.3.28"));
        // 设置Freemarker的编码格式
        configuration.setDefaultEncoding("UTF-8");

        String str = DateUtils.dateTimeNow() + ".doc";
        Writer out = null;
        try{
            // 设置FreeMarker生成Word文档所需要的模板的路径
            configuration.setDirectoryForTemplateLoading(new File(templatePath));
            // 设置FreeMarker生成Word文档所需要的模板名称
            Template t = configuration.getTemplate(templateFile, "UTF-8");
            // 创建一个Word文档的输出流
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(generateFile)), "UTF-8"));
            //FreeMarker使用Word模板和数据生成Word文档
            t.process(dataMap, out);
            //下载生成好的Word文档
            downloadFile(generateFile,str,resp,req);

        } catch (Exception e) {
            e.printStackTrace();
        }
        if (out != null) {
            try {
                out.flush();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /********************************************/
    /**
     * 下载文件
     * @param path 文件的位置
     * @param fileName 自定义下载文件的名称
     * @param resp http响应
     * @param req http请求
     */
    public static void downloadFile(String path, String fileName, HttpServletResponse resp, HttpServletRequest req){
        System.out.println("开始下载到本地");
        try {
            File file = new File(path);
            /**
             * 中文乱码解决
             */
            String type = req.getHeader("User-Agent").toLowerCase();
            if(type.indexOf("firefox")>0 || type.indexOf("chrome")>0){
                /**
                 * 谷歌或火狐
                 */
                fileName = new String(fileName.getBytes("utf-8"), "iso8859-1");
            }else{
                /**
                 * IE
                 */
                fileName = URLEncoder.encode(fileName, "utf-8");
            }
            // 设置响应的头部信息
            resp.setHeader("content-disposition", "attachment;filename=" + fileName);
            // 设置响应内容的类型
            resp.setContentType(getFileContentType(fileName)+"; charset= utf-8");
            // 设置响应内容的长度
            resp.setContentLength((int) file.length());
            // 输出
            outStream(new FileInputStream(file), resp.getOutputStream());
        } catch (Exception e) {
            System.out.println("执行downloadFile发生了异常:" + e.getMessage());
        }
    }

    /**
     * 文件的内容类型
     */
    private static String getFileContentType(String name){
        String result = "";
        String fileType = name.toLowerCase();
        if (fileType.endsWith(".png")) {
            result = "image/png";
        } else if (fileType.endsWith(".gif")) {
            result = "image/gif";
        } else if (fileType.endsWith(".jpg") || fileType.endsWith(".jpeg")) {
            result = "image/jpeg";
        } else if(fileType.endsWith(".svg")){
            result = "image/svg+xml";
        }else if (fileType.endsWith(".doc")) {
            result = "application/msword";
        } else if (fileType.endsWith(".xls")) {
            result = "application/x-excel";
        } else if (fileType.endsWith(".zip")) {
            result = "application/zip";
        } else if (fileType.endsWith(".pdf")) {
            result = "application/pdf";
        } else {
            result = "application/octet-stream";
        }
        return result;
    }


    /**
     * 基础字节数组输出
     */
    private static void outStream(InputStream is, OutputStream os) {
        try {
            byte[] buffer = new byte[10240];
            int length = -1;
            while ((length = is.read(buffer)) != -1) {
                os.write(buffer, 0, length);
                os.flush();
            }
        } catch (Exception e) {
            System.out.println("执行 outStream 发生了异常:" + e.getMessage());
        } finally {
            try {
                os.close();
            } catch (IOException e) {
            }
            try {
                is.close();
            } catch (IOException e) {
            }
        }
    }

    /**
     * 检查存储生成文件的路径是否存在,如果不存在则新建路径.
     * @param directory the directory name, like '\dir-name'
     */
    public static void CheckDownloadPath(String directory){
        File path=new File(RuoYiConfig.getDownloadPath()+directory);
        if(!path.exists()){
            path.mkdirs();
        }
    }


}

7.controller文件(重点)

在controller中调用工具类生成word

/**
     * 导出仪器校准方案列表
     */
    @PreAuthorize("@ss.hasPermi('system:scheme:export')")
    @Log(title = "仪器校准方案", businessType = BusinessType.EXPORT)
    @PostMapping("/export/{ids}")
    public void export(HttpServletResponse resp, HttpServletRequest req, @PathVariable Long[] ids)
    {
        //获取仪器设备检校列表
        List<DataApparatusCheckSchemeVo> list = dataApparatusCheckSchemeService.selectDataApparatusCheckSchemeByIds(ids)
        
        //获取字典
        HashMap<String, ArrayList<SysDictData>> dictData = dictDataService.getAllDictDataMap();

        //生成结果集
        Map<String, Object> dataMap = new HashMap<String,Object>();
        ArrayList<TempDataApparatusCheckScheme> resultList=new ArrayList<TempDataApparatusCheckScheme>();
        for (DataApparatusCheckSchemeVo s:list){
            resultList.add(new TempDataApparatusCheckScheme(s,dictData));
        }
        dataMap.put("schemeList",resultList);
        //设置生成文件存储路径
        String directory="/schemeList";
        WordUtil.CheckDownloadPath(directory);
        //生成文件
        WordUtil.CreateWord(resp,req,dataMap,"ruoyi-system\\src\\main\\resources\\template\\dataApparatusCheckScheme","BKJC-CX-007-2.ftl"
        , RuoYiConfig.getDownloadPath()+directory+"/BKJC-CX-007-2.doc");
        //下载后删除生成文件
        FileUtils.deleteFile(RuoYiConfig.getDownloadPath()+directory+"/BKJC-CX-007-2.doc");

    }

http://t.csdn.cn/iZpPv中描述了获取全部若依全部字典的方法

8.前端文件

js文件

// 导出仪器校准方案
export function exportScheme(ids){
  return request({
    url:'/system/scheme/export/' + ids,
    method: 'post'
  })
}

index.vue

 /** 导出按钮操作 */
    handleExport() {
      let schemeNum=this.ids.length;
      if(schemeNum<1){
        this.$message.error('未选择需要导出的设备仪器检校信息,请勾选需要导出的设备仪器检校信息!');
      }
      else{
        exportScheme(this.ids).then(response =>{
          let data = new Blob([response], { type: 'application/msword,charset=utf-8' });
          let fileDate = new Date();
          let fileYear = fileDate.getFullYear();
          let fileMonth = (fileDate.getMonth()+1).toString().padStart(2,'0');
          let fileDay = fileDate.getDate().toString().padStart(2,'0');
          var fileHour = fileDate.getHours().toString().padStart(2,'0')
          var fileMin = fileDate.getMinutes().toString().padStart(2,'0')
          if (typeof window.chrome !== 'undefined') {undefined
            // chrome
            const link = document.createElement('a');
            link.href = window.URL.createObjectURL(data);
            link.download = fileYear+fileMonth+fileDay+fileHour+fileMin+"仪器设备检校表";
            link.click();
          } else if (typeof window.navigator.msSaveBlob !== 'undefined') {undefine
            // IE
            const blob = new Blob([data], {type: 'application/force-download'});
            window.navigator.msSaveBlob(blob, "BKJC-CX-007-2");
          } else {undefined
            // Firefox
            const file = new File([data], "BKJC-CX-007-2", {type: 'application/force-download'});

            window.open(URL.createObjectURL(file));

          }
        })
      }

三、处理模板文件

1.填好最初的word文件

在这里插入图片描述

2.word文件转xml文件

在这里插入图片描述

3.利用xml文件生成ftl模板文件

将保存好的xml文件用vsCode打开,vsCode安装XML Tools插件用于格式化xml文件。
xml文件格式化后,找到如下图的表格数据
在这里插入图片描述如果${}形式的占位符中的内容被分隔开了,我们需要手动删除掉中间内容。
将所有表格数据处理好后折叠,在外部包裹如下代码:

<#list schemeList as scheme>
</#list>

完成后如下所示:
在这里插入图片描述注意
①schemeList要与controller代码中保持一致
②as是给schemeList起别名的作用
③scheme就是userList的别名,这样在xml中就可以用scheme.

全部完成后将文件另存为ftl格式即可。

最后将word、xml、ftl三个文件放入项目如图位置。其中ftl文件是真正的模板文件,其余两个文件是中间文件,没有实际作用。
在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐