当前位置: 首页 > news >正文

Java之word导出下载

访问我的博客

前言

最近遇到项目需求需要将数据库中的部分数据导出到 word 中,具体是在一个新闻列表中将选中的新闻导出到一个 word 中。参考了网上一些教程,实现了该功能,在此记录下来。

导出结果如下:

mark

图中为导出的其中两条新闻。

搜索网上导出 word 的方式有很多种,但是很多都是一笔带过,有示例代码的只找到了 POI 导出,和通过 FreeMarker 方式导出,但是只是具有参考意义。本文采取使用 FreeMark 方式。

实现步骤

  1. Maven 工程引入FreeMarker 的依赖,非 Maven 工程添加 jar 包
<dependency>
  <groupId>org.freemarker</groupId>
  <artifactId>freemarker</artifactId>
  <version>2.3.26-incubating</version>
</dependency>
  1. 创建 word 模板
    1. 新建 word 替换内容为占位符
      mark

    2. 另存模板为 XML 文件
      mark

    3. 使用 NotePad++ 打开 xml 文件

    4. 选中全部内容,到这里进行格式化

    5. 将原内容替换为格式化后的内容

    6. 因为我的 word 的内容是一个列表,所以需要添加一个 freemarer 标签标识

    7. 找到<w:document> 元素下面的 <w:body> 元素,添加<#list newsList news> , 并在</w:body>结束标签之前闭合 </#list>, 此处的 newsList 为后台读取模板时需要需要渲染数据map集合的key, 其所对应的是一个list集合。
      mark

    8. 保存为 FreeMarker 的模板文件,后缀为 ftl 格式,拷贝到项目中
      mark

  2. 编写代码
    1. 从数据中查询数据集合放入Map中, 调用工具方法,返回流
    Map<String, Object> root = new HashMap<String, Object>();
     root.put("newsList", newsList);//newsList为新闻对象集合
     String template = "/temp.ftl";  //模板文件的地址
     ByteArrayOutputStream outputStream = WordUtil.process(root, template);
     return outputStream;
    1. 调用下载工具类进行下载即可。
    DownloadUtil.download(byteArrayOutputStream, response, returnname);
    注:在实现功能的时候,由于采取的是 ajax 请求方式,导致只是将流写入 Response 时, Response 为 xml 格式的数据。但是想要实现的效果是弹出下载框,下载 word 文档。最后查询资料,修改ajax请求为form表单提交方式(ajax form),才弹出下载框实现了功能。

文章涉及工具类

//WordUtil.java
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Map;

import freemarker.template.Configuration;
import freemarker.template.Template;

public final class WordUtil {
      private static Configuration configuration = null;

      private WordUtil() {
        throw new AssertionError();
      }

      /**
       * 根据模板生成相应的文件
       * @param root 保存数据的map
       * @param template 模板文件的地址
       * @param path 生成的word文档输出地址
       * @return
       */
      public static synchronized ByteArrayOutputStream process(Map<?, ?> root, String template) {

        if (null == root ) {
          throw new RuntimeException("数据不能为空");
        }

        if (null == template) {
          throw new RuntimeException("模板文件不能为空");
        }

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        String templatePath = template.substring(0, template.lastIndexOf("/"));
        String templateName = template.substring(template.lastIndexOf("/") + 1, template.length());

        if (null == configuration) {
          configuration = new Configuration(Configuration.VERSION_2_3_23);  // 这里Configurantion对象不能有两个,否则多线程访问会报错
          configuration.setDefaultEncoding("utf-8");
          configuration.setClassicCompatible(true);
        }
        configuration.setClassForTemplateLoading(WordUtil.class, templatePath);

        Template t = null;
        try {
          t = configuration.getTemplate(templateName);
          Writer w = new BufferedWriter(new OutputStreamWriter(outputStream, "utf-8"));
          t.process(root, w);  // 这里w是一个输出地址,可以输出到任何位置,如控制台,网页等
          w.close();
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
        return outputStream;
      }

}
//DownloadUtil.java
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;

public class DownloadUtil {

    /**
     * @param byteArrayOutputStream 将文件内容写入ByteArrayOutputStream
     * @param response HttpServletResponse  写入response
     * @param returnName 返回的文件名
     */
    public static void download(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) throws IOException{
        response.setContentType("application/msword");
        response.setHeader("Content-Disposition", "attachment; filename=" + returnName);
        response.setContentLength(byteArrayOutputStream.size());
        OutputStream outputstream = response.getOutputStream();         //取得输出流
        byteArrayOutputStream.writeTo(outputstream);                    //写到输出流
        byteArrayOutputStream.close();                                  //关闭
        outputstream.flush();                                           //刷数据
    }
}

源码下载

点我下载

参考链接

  • http://itindex.net/detail/55080-springboot-freemarker-%E6%A0%BC%E5%BC%8F

转载于:https://www.cnblogs.com/vcmq/p/9484362.html

相关文章:

  • bootstrap完美实现5列布局
  • 第二章 JAVA语言基本语法————数据类型之间的转换
  • 爱奇艺大数据招聘
  • 记一次element-ui组件开发经历
  • 初遇博客园
  • 知乎收藏—曾国藩
  • C++开发浏览器插件ActiveX(一)
  • Shell脚本进阶(下)
  • SharePoint 2013 母版页取消和HTML页关联
  • 小邵教你玩转Generator+co/async await
  • find命令使用详解
  • NIO
  • 如果是你,你会怎样回答?
  • JQuery -- this 和 $(this) 的区别
  • 【原创】RabbitMQ 之 no_ack 分析
  • 【前端学习】-粗谈选择器
  • 2019.2.20 c++ 知识梳理
  • android百种动画侧滑库、步骤视图、TextView效果、社交、搜房、K线图等源码
  • Bootstrap JS插件Alert源码分析
  • create-react-app做的留言板
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • learning koa2.x
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • Terraform入门 - 1. 安装Terraform
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 从0到1:PostCSS 插件开发最佳实践
  • 大主子表关联的性能优化方法
  • 猴子数据域名防封接口降低小说被封的风险
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • 在electron中实现跨域请求,无需更改服务器端设置
  • 《天龙八部3D》Unity技术方案揭秘
  • Hibernate主键生成策略及选择
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • ​TypeScript都不会用,也敢说会前端?
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • (java)关于Thread的挂起和恢复
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .dwp和.webpart的区别
  • .NET Core 2.1路线图
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .NET企业级应用架构设计系列之技术选型
  • /bin/rm: 参数列表过长"的解决办法
  • @test注解_Spring 自定义注解你了解过吗?
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [ 数据结构 - C++]红黑树RBTree
  • [ 英语 ] 马斯克抱水槽“入主”推特总部中那句 Let that sink in 到底是什么梗?
  • [ACTF2020 新生赛]Upload 1
  • [bzoj1901]: Zju2112 Dynamic Rankings