java response 输出word_如何使用java代码导出word
前言:
导出word的需求其实在日常工作中用到的地方还不少,于是想写一篇文章好好记录一下,在导出之前,需要了解一下关于浏览器如何处理servlet的后台数据。具体可以了解一下http通信下载行为在servlet的实现。
==导出的工具类代码来源于网络,如有侵权可以联系我删除文章==
个人使用==ftl==作为word导出模板引擎,有很多模板引擎可以选,个人经过查阅资料发现ftl用的比较多,所以选择这一种
码云地址:
文章牵扯代码比较多,如果要看具操作可以查看我自己瞎弄的一个码云地址:
效果演示:
给了一个测试页面,临时写了一些脚本,可以作为参考(后续会贴Html代码进去)
点击提交,导出内容, 导出word报告
导出之后,打开word内容为:
实现步骤 - 制作word模板
第一步 新建word,制作成果样板
将需要导出word的内容,先粘贴到一个新建的word文件里面
第二步 转存格式 -> xml
选择文件“另存为”,将格式设置为xml格式
第三步 格式化文件
将文件放到idea或者支持格式化的软件里面,进行格式化,保存:
注意占位符要匹配
第四步:模板数据替换占位符
在word页面将需要导入数据的地方,替换占位符需要注意内容处理的时候: ${ filename} 有可能被切割为多个部分,我们需要把多个切割部分,改为下面的样式
一定记得所有的改动之后,马上打开xml格式的word,确认是不是改崩了
上面的步骤完成,说明有一个word模板做好了
第五步:制作ftl文件,word模板成型
在项目里面新建一个ftl文件,同时需要在工具类中配置,同时把做好站位符操作的xml内容贴进去
代码实现 - 导出代码工具类的配置如下:
WordGeneratorUtil.java:/**
* 模板常量类配置
*/
public static final class FreemarkerTemplate {
public static final String REPORT = "report";
public static final String REC_RECOMMEND = "recRecommend";
// 增加你的模板文件名称:
}
在静态的代码块里面,需要注入对应的模板配置// 注意初始化要载入对应模板
allTemplates.put(FreemarkerTemplate.REPORT, configuration.getTemplate(FreemarkerTemplate.REPORT + ".ftl"));
allTemplates.put(FreemarkerTemplate.REC_RECOMMEND,configuration.getTemplate(FreemarkerTemplate.REC_RECOMMEND + ".ftl"));在配置完成之后,导出的时候就可以找到对应的文件了
建立一个通用的导出方法:/**
* 创建doc 文档
* dataMap 数据,需要对应模板的占位符,否则会出错
* @param dataMap 数据
* @param wordName word 报表的名称
* @param freemarkerTemplateName 指定需要使用哪个freemarker模板
* @return
*/
public static File createDoc(String freemarkerTemplateName, String wordName, Map dataMap) {
try {
File f = new File(wordName);
Template t = allTemplates.get(freemarkerTemplateName);
// 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开
Writer w = new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8);
t.process(dataMap, w);
w.close();
return f;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException("生成word文档失败");
}
}
工具类完整代码:package com.zxd.interview.export;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.util.CollectionUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
*
* 从网络上根据资料找到的一个工具类
* 主要以freemarker 为核心的模板生成word文档的工具类
* 这里默认配置了固定路径
* 需要根据路径取到对应模板
* 请求参数需要设置对应的模板名称
* @author
* @className: WordGeneratorUtils
* @description: 文档生成工具类
*
* version: V1.0.0
*/
public final class WordGeneratorUtil {
private static Configuration configuration = null;
private static Map allTemplates = null;
private static final String TEMPLATE_URL = "/templates";
/**
* 模板常量类配置
*/
public static final class FreemarkerTemplate {
public static final String Test = "test";
public static final String REPORT = "report";
public static final String REC_RECOMMEND = "recRecommend";
}
static {
configuration = new Configuration(Configuration.VERSION_2_3_28);
configuration.setDefaultEncoding("utf-8");
configuration.setClassForTemplateLoading(WordGeneratorUtil.class, TEMPLATE_URL);
allTemplates = new HashMap(4);
try {
// 注意初始化要载入对应模板
allTemplates.put(FreemarkerTemplate.Test, configuration.getTemplate(FreemarkerTemplate.Test + ".ftl"));
allTemplates.put(FreemarkerTemplate.REPORT, configuration.getTemplate(FreemarkerTemplate.REPORT + ".ftl"));
allTemplates.put(FreemarkerTemplate.REC_RECOMMEND, configuration.getTemplate(FreemarkerTemplate.REC_RECOMMEND + ".ftl"));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private WordGeneratorUtil() {
throw new AssertionError();
}
/**
* 创建doc 文档
* dataMap 数据,需要对应模板的占位符,否则会出错
* @param dataMap 数据
* @param wordName word 报表的名称
* @param freemarkerTemplateName 指定需要使用哪个freemarker模板
* @return
*/
public static File createDoc(String freemarkerTemplateName, String wordName, Map dataMap) {
try {
File f = new File(wordName);
Template t = allTemplates.get(freemarkerTemplateName);
// 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开
Writer w = new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8);
t.process(dataMap, w);
w.close();
return f;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException("生成word文档失败");
}
}
}
调用层:在业务层,将需要导出的数据,根据占位符的i信息进行赋值,注意不能漏,否则导出之后的文件会打不开@Override
public File exportQualityStep4Word(WordReportDTO exportWordRequest) {
Map datas = new HashMap(QualityConstants.HASH_MAP_INIT_VALUE);
//主标题
datas.put("schoolName", exportWordRequest.getSchoolName());
datas.put("title1", exportWordRequest.getBaseSituation());
datas.put("title2", exportWordRequest.getLearningEnvRec());
datas.put("title3", exportWordRequest.getLearningEnvPro());
datas.put("title4", exportWordRequest.getDayLifeRec());
datas.put("title5", exportWordRequest.getDayLifePro());
datas.put("title6", exportWordRequest.getLearningActivityRec());
datas.put("title7", exportWordRequest.getLearningActivityPro());
datas.put("title8", exportWordRequest.getDevRecommend());
datas.put("base64_1", exportWordRequest.getBase64_1());
datas.put("base64_2", exportWordRequest.getBase64_2());
datas.put("base64_3", exportWordRequest.getBase64_3());
datas.put("base64_4", exportWordRequest.getBase64_4());
datas.put("base64_5", exportWordRequest.getBase64_5());
datas.put("base64_6", exportWordRequest.getBase64_6());
//导出
return WordGeneratorUtil.createDoc(WordGeneratorUtil.FreemarkerTemplate.REPORT,
exportWordRequest.getWordName(),
datas);
}下面是生成报表导出的基本操作,可以在用到的地方复制过去改动即可/**
* 生成报告的导出报表操作
*
* @param request request
* @param response 响应数据
* @param exportWordRequest 导出dto
*/
@PostMapping("/quality/exportword")
@ResponseBody
public void povertyExportWord(HttpServletRequest request, HttpServletResponse response,
WordReportDTO exportWordRequest) {
File file = qualityReportService.exportQualityStep4Word(exportWordRequest);
InputStream fin = null;
OutputStream out = null;
try {
// 调用工具类WordGeneratorUtils的createDoc方法生成Word文档
fin = new FileInputStream(file);
response.setCharacterEncoding(QualityConstants.UTF_8);
response.setContentType(QualityConstants.CONTENT_TYPE_WORD);
// 设置浏览器以下载的方式处理该文件
// 设置文件名编码解决文件名乱码问题
//获得请求头中的User-Agent
String filename = exportWordRequest.getWordName();
String agent = request.getHeader(QualityConstants.USER_AGENT);
String filenameEncoder = "";
// 根据不同的浏览器进行不同的判断
if (agent.contains(QualityConstants.MSIE)) {
// IE浏览器
filenameEncoder = URLEncoder.encode(filename, QualityConstants.UTF_8);
filenameEncoder = filenameEncoder.replace("+", " ");
} else if (agent.contains(QualityConstants.FIREFOX)) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filenameEncoder = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes(QualityConstants.UTF_8)) + "?=";
} else {
// 其它浏览器
filenameEncoder = URLEncoder.encode(filename, QualityConstants.UTF_8);
}
response.setHeader(QualityConstants.ACCESS_CONTROL_ALLOW_ORIGIN, "*");//所有域都可以跨
response.setHeader(QualityConstants.CONTENT_TYPE, QualityConstants.CONTENT_TYPE_STEAM);//二进制 流文件
response.setHeader(QualityConstants.CONTENT_DISPOSITION, "attachment;filename=" + filenameEncoder + ".doc");//下载及其文件名
response.setHeader(QualityConstants.CONNECTION, QualityConstants.CLOSE);//关闭请求头连接
//设置文件在浏览器打开还是下载
response.setContentType(QualityConstants.CONTENT_TYPE_DOWNLOAD);
out = response.getOutputStream();
byte[] buffer = new byte[QualityConstants.BYTE_512];
int bytesToRead = QualityConstants.NUM_MINUS_1;
// 通过循环将读入的Word文件的内容输出到浏览器中
while ((bytesToRead = fin.read(buffer)) != QualityConstants.NUM_MINUS_1) {
out.write(buffer, QualityConstants.NUM_ZERO, bytesToRead);
}
} catch (Exception e) {
throw new RuntimeException(QualityConstants.FARIURE_EXPORT, e);
} finally {
try {
if (fin != null) {
fin.close();
}
if (out != null) {
out.close();
}
if (file != null) {
file.delete();
}
} catch (IOException e) {
throw new RuntimeException(QualityConstants.FARIURE_EXPORT, e);
}
}
}
导出实体dto
下面写了一个导出的实体dto,实体对象可以自己定制:package com.zxd.interview.dto;
/**
* 测试使用的dto,用于封装导出word的对象
*
* @author zhaoxudong
* @version 1.0
* @date 2020/11/7 23:37
*/
public class TestReportDTO {
/**
* 测试
*/
private String test0;
/**
* 测试
*/
private String test1;
/**
* 测试
*/
private String test2;
/**
* 测试
*/
private String test4;
/**
* 测试
*/
private String test5;
/**
* 测试
*/
private String test6;
/**
* 报告名称
*/
private String wordName;
public String getTest0() {
return test0;
}
public void setTest0(String test0) {
this.test0 = test0;
}
public String getTest1() {
return test1;
}
public void setTest1(String test1) {
this.test1 = test1;
}
public String getTest2() {
return test2;
}
public void setTest2(String test2) {
this.test2 = test2;
}
public String getTest4() {
return test4;
}
public void setTest4(String test4) {
this.test4 = test4;
}
public String getTest5() {
return test5;
}
public void setTest5(String test5) {
this.test5 = test5;
}
public String getTest6() {
return test6;
}
public void setTest6(String test6) {
this.test6 = test6;
}
public String getWordName() {
return wordName;
}
public void setWordName(String wordName) {
this.wordName = wordName;
}
@Override
public String toString() {
return "TestReportDTO{" +
"test0='" + test0 + '\'' +
", test1='" + test1 + '\'' +
", test2='" + test2 + '\'' +
", test4='" + test4 + '\'' +
", test5='" + test5 + '\'' +
", test6='" + test6 + '\'' +
'}';
}
}
常量配置模块:
个人很不喜欢硬编码这东西,又丑又难看,所以很多东西会用不可变对象替代.package com.zxd.interview.constant;
/**
* 常量配置类
*
* @author zhouhui
*/
public class QualityConstants {
/**
* 质量检测 的督导事项id
*/
public static final int EVENTID = 12;
/**
* 数字0
*/
public static final int NUM_ZERO = 0;
/**
* 数字1
*/
public static final int NUM_ONE = 1;
/**
* 数字2
*/
public static final int NUM_TWO = 2;
/**
* 数字-1
*/
public static final int NUM_MINUS_1 = -1;
/**
* 字节大小512
*/
public static final int BYTE_512 = 512;
/**
* 500错误编码
*/
public static final int CODE_500 = 500;
/**
* 500错误提示信息 - 状态非法
*/
public static final String CODE_500_MSG_1 = "状态非法!";
/**
* 500错误提示信息 - 非督导用户不允许查看质量检测记录
*/
public static final String CODE_500_MSG_2 = "非督导用户不允许查看质量检测记录!";
/**
* 500错误提示信息 - 这条质量监测已经完成!无法修改
*/
public static final String CODE_500_MSG_3 = "这条质量监测已经完成!无法修改!";
/**
* 500错误提示信息 - 提交失败,材料上传不能为空
*/
public static final String CODE_500_MSG_4 = "提交失败,材料上传不能为空";
/**
* 500错误提示信息 - 提交失败,请稍后重试或联系管理员
*/
public static final String CODE_500_MSG_5 = "提交失败,请稍后重试或联系管理员!";
/**
* 500错误提示信息 - 提交失败,意见反馈不能为空
*/
public static final String CODE_500_MSG_6 = "提交失败,意见反馈不能为空!";
/**
* 405错误编码
*/
public static final int CODE_405 = 405;
/**
* 405错误提示信息 - 该信息只允许督导查看
*/
public static final String CODE_405_MSG_1 = "该信息只允许督导查看!";
/**
* 200成功编码
*/
public static final int CODE_200 = 200;
/**
* 200成功提示信息 - 该信息只允许督导查看
*/
public static final String CODE_200_MSG_1 = "提交成功!";
/**
* 错误提示信息 - 尚未选择记录
*/
public static final String DELETE_FAIRURE_MSG = "删除失败,尚未选择记录!";
/**
* 错误提示信息 - 尚未选择记录
*/
public static final String NO_RECORD_SELECTED = "尚未选择记录!";
/**
* 字符编码utf-8
*/
public static final String UTF_8 = "utf-8";
/**
* 默认pid
*/
public static final int PID = 0;
/**
* 默认层级
*/
public static final int DEFUALT_LAYER = 1;
/**
* 不适当最低得分
*/
public static final Integer MIN_SCORE = 1;
/**
* 优秀最高得分
*/
public static final Integer MAX_SCORE = 7;
/**
* map的hash初始值
*/
public static final int HASH_MAP_INIT_VALUE = 32;
/**
* 全园平均分
*/
public final static String WHOLE_AVERAGE = "全园平均分";
/**
* 查询失败
*/
public final static String QUERY_FAIRURE = "查询失败";
/**
* 操作成功
*/
public final static String SUCCESS_MSG = "操作成功!";
/**
* 操作失败
*/
public final static String FARIURE_MSG = "操作失败!";
/**
* 导出失败
*/
public final static String FARIURE_EXPORT = "导出失败!";
/**
* 请求头 - 文档
*/
public final static String CONTENT_TYPE_WORD = "application/msword";
/**
* 请求头 - 下载
*/
public final static String CONTENT_TYPE_DOWNLOAD = "application/x-download";
/**
* 请求头 - 二进制文件
*/
public final static String CONTENT_TYPE_STEAM = "application/octet-stream;charset=UTF-8";
/**
* 请求头
*/
public final static String USER_AGENT = "User-Agent";
/**
* 请求头
*/
public final static String CONTENT_TYPE = "Content-Type";
/**
* 连接
*/
public final static String CONNECTION = "Connection";
/**
* 关闭连接
*/
public final static String CLOSE = "close";
/**
* 连接
*/
public final static String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
/**
* 连接
*/
public final static String CONTENT_DISPOSITION = "Content-Disposition";
/**
* 浏览器 - ie
*/
public final static String MSIE = "MSIE";
/**
* 浏览器 - Firefox
*/
public final static String FIREFOX = "Firefox";
/**
* 填写报告的step
*/
public final static String MODULE_STEP3_REPORT = "qualityreport";
/**
* 督导下园核实的材料
*/
public final static String MODULE_STEP1_MATERIAL = "qualitymetrail";
/**
* 数字3
*/
public final static int NUM_3 = 3;
/**
* 数字4
*/
public final static int NUM_4 = 4;
/**
* 数字5
*/
public final static int NUM_5 = 5;
/**
* 数字6
*/
public final static int NUM_6 = 6;
/**
* 数字7
*/
public final static int NUM_7 = 7;
/**
* 数字8
*/
public final static int NUM_8 = 8;
/**
* 数字9
*/
public final static int NUM_9 = 9;
/**
* 数字10
*/
public final static int NUM_10 = 10;
/**
* 数字11
*/
public final static int NUM_11 = 11;
/**
* 数字12
*/
public final static int NUM_12 = 12;
/**
* 数字13
*/
public final static int NUM_13 = 13;
/**
* 数字14
*/
public final static int NUM_14 = 14;
/**
* 数字15
*/
public final static int NUM_15 = 15;
/**
* 数字16
*/
public final static int NUM_16 = 16;
/**
* 数字17
*/
public final static int NUM_17 = 17;
/**
* 数字18
*/
public final static int NUM_18 = 18;
/**
* 数字19
*/
public final static int NUM_19 = 19;
/**
* 数字20
*/
public final static int NUM_20 = 20;
/**
* 格式化数字
*/
public final static String DECIMAL_Format = "######.00";
}
页面层处理:
前端增加一个form提交,使用form提交表单数据,实现word导出功能:
(注意使用的模板引擎是thymeleaf)
html代码:
Title
导出word名称:
test0:
test1:
test2:
test4:
test5:
test6:
js代码
使用js代码处理form表单提交,使用了jquery进行导出,其实一直不太懂前端怎么导出后台产生的二进制流,做法挺多,下次写一篇文章好好汇总一下几种用法。var v1 = new Vue({
el: '#vue1',
data: {
counter: 0,
student: {
test0:'',
test1:'',
test2:'',
test3:'',
test4:'',
test5:'',
test6:'',
wordName: '',
}
},
methods: {
test: function () {
console.log(this.counter);
},
submit() {
console.log(this.student);
var url = '/quality/exportword';
var formData = JSON.stringify(this.student); // this指向这个VUE实例 data默认绑定在实例下的。所以直接this.student就是要提交的数据
this.$http.post(url, formData).then(function (data) {
console.log(data);
let blob = new Blob([data.data],{ type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=utf-8'});
let objectUrl = URL.createObjectURL(blob);
window.location.href = objectUrl;
}).catch(function () {
console.log('test');
});
}
}
})
结尾:
个人水平一般,希望通过这篇文章可以帮到读者,有错误的地方欢迎指点,看到会及时改成,谢谢!
前段时间忙于面试找到新的地方工作了,等工作安定之后,会继续深耕博客和技术栈。
java response 输出word_如何使用java代码导出word相关推荐
- java控制台输出五行字符串_java五行代码导出Excel
已经写过两种Excel导出插件了.今天再安利一个极简的导出Excel的框架,导出无特殊格式要求的Excel,只需五行代码: 先看代码 再看效果 EasyExcel 本案例用到的框架是阿里推出的Easy ...
- java将后台数据库查询到的数据导出word文档当中
java将后台数据库查询到的数据导出word文档当中 之前项目需求使用Java导出word文档,一直没有进行整理,今天把它进行整理出来,以便以后使用到:下面是导出的word文档. // 前端报告表格 ...
- java打出的代码如何保存_如何使用java代码导出word
前言: 导出word的需求其实在日常工作中用到的地方还不少,于是想写一篇文章好好记录一下,在导出之前,需要了解一下关于浏览器如何处理servlet的后台数据.具体可以了解一下http通信下载行为在se ...
- 如何使用java代码导出word
前言: 导出word的需求其实在日常工作中用到的地方还不少,于是想写一篇文章好好记录一下,在导出之前,需要了解一下关于浏览器如何处理servlet的后台数据.具体可以了解一下http通信下载行为在se ...
- 用java的输出姓名_用java程序输出自己的姓名
代码部分: public class Hello { public static void main(String[] args) { System.out.println("$$$$$$$ ...
- java中输出a个b_下面代码输出什么 ( ) var a=0,b=0; for(;a10,b7;a++,b++){ g=a+b; } console.log(g);_学小易找答案...
[单选题]Java Script 函数说法正确的是 () [单选题]阅读下面的 Javascript 代码 , 输出结果正确的是( ) var i=0; for(i=0;i<=5;i++){ i ...
- java poi替换word_利用POI 技术动态替换word模板内容
项目中需要实现一个功能,动态替换给定模板里面的内容,生成word文档提供下载功能. 中间解决了问题有: 1.页眉的文档logo图片解决,刚开始的时候,HWPFDocument 对象无法读取图片对象(已 ...
- java怎样输出一个文件夹,java合并一个文件夹下所有txt文件,输出到另一个txt,...
java合并一个文件夹下所有txt文件,输出到另一个txt,最近写了个单元测试,递归调用方法,把同一个文件夹里所有的txt合并输出到一个txt文件.参考了两个博客,分别是已有的方法,还有个就是检测tx ...
- 00005在java结果输出_浅谈Java反序列化漏洞原理(案例未完善后续补充)
摘要: 0005,这个16进制流基本上也意味者java反序列化的开始:(2)HTTP:必有rO0AB,其实这就是aced0005的base64编码的结果:以上意味着存在Java反序列化,可尝试构造pa ...
最新文章
- Oracle数据库——SQL高级查询
- 与php有区别_php://output和php://stdout的区别
- 推荐北大饶毅教授的《生物学概念与途径》课程(慕课视频+讲义)
- php 生命变量,深入理解PHP原理之变量生命期(一)
- 诺基亚:Symbian 3操作系统拥有多点触控功能
- 解决IE9下JQuery的ajax失效的问题
- OpenCV3.1.0+VS2013测试程序
- Jmeter接口测试流程详解(中科软测认证中心)
- 微信html游戏怎么作弊,微信小游戏跳一跳作弊技巧 跳一跳作弊方法介绍
- Prince和学生们侃侃而谈系列06
- solidity 中的时间_Solidity官方文档中文版.pdf
- java如何在窗口上显示数据_如何把Java程序窗口在屏幕中间显示
- 前端性能优化 七个方面
- 项目成本管理名词解析
- mysql中的mul
- 使用python开发 百度网盘接口
- ccv的安装以及测试
- python怎么去掉末尾标点符号_python中怎么去掉标点符号
- 存不存?——中国银行笑话
- java jama_Java调用jama实现矩阵运算
热门文章
- Shopify免费产品评价应用 Product Review安装和设置教程
- [Android 之美] 那些你不知道的APK 瘦身,让你的APK更小
- img预加载获取图片大小方法
- 【论文分享】★★★「SOTA」小样本图神经网络分类模型 HGNN:Hybrid Graph Neural Networks for Few-Shot Learning
- 真正的通过手机控制PPT播放
- AMD意外泄漏下一代APU信息
- RAII (Resource Acquirement Is Initialization)
- C++的time_t 和 struct tm 类型【s
- web应用票据打印实现(四)
- 风口上的“低代码”:是技术变革?还是另一个风险敞口?