java使用freemaker自定义路径导出Word关键点讲解版

最近工作遇到需要开发java导出Word操作,这里我选用的模板是freemaker。

导出的前期操作在网络上一找一大把,我这里复述下我的操作:

1.将要导出的模板打到Word中,然后另存为xml格式的文件。

文档中要作为动态替换的内容信息,需要使用${}包住,值得注意的是,在Word文档中,如果Word识别认为需要替换的部分存在语病,被波浪线标注出来了,那么在转换为xml格式的时候,会被拆开,无法被当做一个字段来处理,所以需要在转换xml之前的Word中,将语病去除修改好。 

2.将修改好的xml文件,以ftl的格式保存,当做在代码中使用的模板,放在静态文件目录中

3.下面就是导出部分,我们首先实现代码中拼装字段,输出到本地中

public void createDoc(Map<String, Object> dataMap, String fileName) throws UnsupportedEncodingException {
        //dataMap 要填入模本的数据文件
        //设置模本装置方法和路径,FreeMarker支持多种模板装载方法。可以重servlet,classpath,数据库装载,
        //这里我们的模板是放在template包下面
        configuration.setClassForTemplateLoading(this.getClass(), "/template");
        Template t = null;
        try {
            //occupationalHealth.ftl为要装载的模板
            t = configuration.getTemplate("occupationalHealth.ftl");
        } catch (IOException e) {
            e.printStackTrace();
        }
        //输出文档路径及名称
        File outFile = new File(fileName);
        //如果不存在临时路径则创建
        if (!outFile.getParentFile().exists()) {
            outFile.getParentFile().mkdirs();
        }
        Writer out = null;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(outFile);
            OutputStreamWriter oWriter = new OutputStreamWriter(fos, "UTF-8");
            //这个地方对流的编码不可或缺,使用main()单独调用时,应该可以,但是如果是web请求导出时导出后word文档就会打不开,并且包XML文件错误。主要是编码格式不正确,无法解析。
            //out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));
            out = new BufferedWriter(oWriter);
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }

        try {
            t.process(dataMap, out);
            out.close();
            fos.close();
        } catch (TemplateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
   }

 我个人认为这里的写法有很多,网上能搜索出很多大同小异的操作,核心方法就是 ,先加载本地ftl模板,成为输出流的形式,再通过process方法将传入的要替换的参数与模板处理成Word文件输出到指定的目录,这里我的参数也贴一下:

Map<String, Object> dataMap = new HashMap<String, Object>();
        dataMap.put("depart", exportDomain.getEnterpriseName());
        dataMap.put("factory", exportDomain.getWorkShop());
        dataMap.put("employee", exportDomain.getName());
        dataMap.put("year", "2021");
        dataMap.put("month", "08");
        dataMap.put("day", "31");
        dataMap.put("medical", "motoGP");

        exportAndPrintService.createDoc(response,dataMap, "D:/outFile.doc");

 其实上面便是输出到本地的几个关键点的罗列,下面,如果想以io流的形式输出到前端,供我们下载,则需要做一些调整:

首先,createDoc方法不能是void类型了,需要设置为file类型:

public File createDoc(Map<String, Object> dataMap, String fileName) throws UnsupportedEncodingException {
        //dataMap 要填入模本的数据文件
        //设置模本装置方法和路径,FreeMarker支持多种模板装载方法。可以重servlet,classpath,数据库装载,
        //这里我们的模板是放在template包下面
        configuration.setClassForTemplateLoading(this.getClass(), "/template");
        Template t = null;
        try {
            //occupationalHealth.ftl为要装载的模板
            t = configuration.getTemplate("occupationalHealth.ftl");
        } catch (IOException e) {
            e.printStackTrace();
        }
        //输出文档路径及名称
        File outFile = new File(fileName);
        //如果不存在临时路径则创建
        if (!outFile.getParentFile().exists()) {
            outFile.getParentFile().mkdirs();
        }
        Writer out = null;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(outFile);
            OutputStreamWriter oWriter = new OutputStreamWriter(fos, "UTF-8");
            //这个地方对流的编码不可或缺,使用main()单独调用时,应该可以,但是如果是web请求导出时导出后word文档就会打不开,并且包XML文件错误。主要是编码格式不正确,无法解析。
            //out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));
            out = new BufferedWriter(oWriter);
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }

        try {
            t.process(dataMap, out);
            out.close();
            fos.close();
            return outFile;
        } catch (TemplateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

再最后我们返回了文档路径与名称,这是为后面能够刷出到页面返回了重要的内容

接下来就是关键的一点,也是困扰我很久,怎么将本地生成的文件以流的形式刷 到前端,方法如下:

public void exportMillCertificateWord(HttpServletResponse response, Map map, String fileName) throws IOException {
        File file = null;
        InputStream fin = null;
        ServletOutputStream out = null;
        try {
            file = createDoc(map, fileName);
            fin = new FileInputStream(file);
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/msword");
            response.setHeader("Content-Disposition", "attachment;filename=".concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
            out = response.getOutputStream();
            byte[] buffer = new byte[512]; // 缓冲区
            int bytesToRead = -1;
// 通过循环将读入的Word文件的内容输出到浏览器中
            while ((bytesToRead = fin.read(buffer)) != -1) {
                out.write(buffer, 0, bytesToRead);
            }
        } finally {
            if (fin != null) {
                fin.close();
            }
            if (out != null) {
                out.close();
            }
            if (file != null) {
                file.delete(); // 删除临时文件
            }
        }

    }

 这个方法首先接收到map,与filename参数,为了能够返回页面,也要定义response。然后在方法中调用刚才写的加载模板与参数,生成本地文档的方法,拿到生成的文档后,我们再将其读到返回值中返回给前端,也就是说,freemaker生成Word读到前端,需要一个临时地址来存放文档,读完后,再由程序删除即可,本地调试可以是本地磁盘,服务器上可以是部署路径,这一点是我研究了一天才弄明白的,我认为这是整个导出操作的关键点所在,导出网上有大把的案例,前端处理网上也有大把案例,但是这个关键点,我没有看到讲述过,记录下来,分享一下。

也顺便贴一下前端处理这个来之不易的输出流的方法:

function exportMethod(data) {
  axios({
    method: 'get',
    url: baseUrl + data.url,//接口地址
    responseType: 'blob',
    headers: {
      'Content-Type': 'application/json'
    }
  }).then((res) => {
    let type = "", type2 = []
    if (data.type == "doc") {
      type = 'application/msword'
      type2 = ['.doc', '.docx']
    }
    const link = document.createElement('a')
    let blob = new Blob([res.data], { type })
    link.style.display = 'none'
    link.href = URL.createObjectURL(blob)
    link.download = data.fileName 
    link.type = type2
    document.body.appendChild(link)
    link.click()
  }).catch(error => {
    console.log(error)
  })
}

前端是vue写的,相信不同的工具的编写方式也是一搜一大把,至于自定义下载路径,则是浏览器的配置问题了。

工作之余匆匆总结下,有问题欢迎指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值