java后台导出word,详细过程及趟过的坑
一、我使用的java导出word,由XML+FreeMarker来实现的,因此需要以下工具:
1、office。这里不能使用wps,因为wps由word文档转为xml文档时,解析会发生错误,导致最终模板和设想的样子有一定的区别。
2、xml编辑工具,这里我推荐使用Firstobject free XML editor。
3、引用FreeMarker的jar包。
<dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.23</version></dependency>
二、模板制作
1、模板制作一定要谨慎,最好是样式全部调试好了,位置都OK了,需求基本不会变了再制作,因为每次修改哪怕一点点东西,都有可能导致全部从头再来一次,非常麻烦。
2、尽量使用office制作,不要用wps制作,然后再用office开发,因为这两者编码和解析方式都有很大的不同,可能导致你导出的word样式变形。
3、如果导出的样式涉及到了图片(多张图片)时,一定要使用小图片制作,最好只有几十K的,不要使用大图片然后缩小,没用的,base64还是会占那么多位置,替换图片时长到你怀疑人生。二是,如果最终导出是有多张图片的,一定要使用多张图片,如果是复制粘贴的,最终xml里只有一段base64,给替换造成非常大的麻烦。
4、如果调试好自己要的所有样式后,另存为Word 2003XML,wps没有这个模式,只有xml格式,使用这个模式最终效果并不理想。转好xml格式后,打开Firstobject free XML editor,直接将xml扔进去就行了,F8调整格式,左边类似目录层级(只用看body就行了),右边为具体源码。
5、替换源码里相应位置的信息换成freemarker的标识。其实就是Map<String, Object>中key。如:姓名替换为${name}
6、 如果还有地方需要循环使用时,可以使用:<#list maps as map></#list> maps是Map<String, Object>中key,值为数组,map为自定义;如果有的内容需要一定条件下使用时,可以使用:<#if name??></#if>,判断循环是否为空:<#if orgList?? && (orgList?size > 0) > </#if>。
7、图片处理(我认为最麻烦的地方)。
(1)在加入了图片占位的地方,会看到一片base64编码后的代码,把base64替换成"{image},也就是Map<String, Object>中key,值必须要处理成base64。(意味着传入的值必须是base64)
(2)这时如果不对模板图片大小做处理,导出来的图片大小就是你模板的大小,基本就是变形+严重缩小,因此还需要对图片大小进行替换。如:<v:shape id="_x0000_i1030" type="#_x0000_t75" style=“width:60pt;height:60pt”>中的60pt整体替换为你输入的值,替换后为:<v:shape id="_x0000_i1030" type="#_x0000_t75" style=“width:{problemsImagesList.w4};height:{problemsImagesList.h4}”>,这里的w4和h4为自己图片大小。
(3)如果你的图片需要循环输出,你还需要改动图片的引用,如果不改动,第二组循环图片还是第一组的。如:
<w:binData w:name="{“wordml://0200000”+0000086+".jpg"}" 和<v:imagedata src="{“wordml://0200000”+0000086+".jpg"}" o:title=“d”/>中的00000086替换掉,替换换后为:<w:binData w:name="{“wordml://0200000”+problemsImagesList_index+4+".jpg"}" 和<v:imagedata src="{“wordml://0200000”+problemsImagesList_index+4+".jpg"}" o:title=“d”/>,这里叫什么无所谓,只要上下两处引用相同,和这一次循环的其他东方图片不同即可。
注意这里有美元符号,不懂为什么放到文章中,会隐藏很多,因此全部美元符号都去掉了
8、至此模板准备基本就OK啦,只需要在Firstobject free XML editor里另存为flt格式就行了,注意flt不要再用word打开了,如果你打开了,那么恭喜你,模板从头再来一次吧。
三、接下来就是在java里组装你需要导入到模板里的数据啦,其实我个人认为,模板只要为问题,这一步就很轻松了,使用的几个工具类我就直接扔出来就行了。
1、生成word的doc格式的类
在这里插入代码片package com.gykj.utils;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Map;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import freemarker.template.Configuration;
import freemarker.template.Template;public class WordUtils {//配置信息,代码本身写的还是很可读的,就不过多注解了 private static Configuration configuration = null; //这里注意的是利用WordUtils的类加载器动态获得模板文件的位置 private static final String templateFolder = WordUtils.class.getClassLoader().getResource("").getPath(); //"D:/patrol";////private static final String templateFolder = "H:/我的项目/lm/lm/web/src/main/webapp/WEB-INF/templates"; static { configuration = new Configuration(); configuration.setDefaultEncoding("utf-8"); try { configuration.setDirectoryForTemplateLoading(new File(templateFolder)); } catch (IOException e) { e.printStackTrace(); } } private WordUtils() { throw new AssertionError(); } public static void exportMillCertificateWord(HttpServletRequest request, HttpServletResponse response, Map map,String title,String ftlFile,String flieUrl) throws IOException { Template freemarkerTemplate = configuration.getTemplate(ftlFile); File file = null; InputStream fin = null; //ServletOutputStream out = null; FileOutputStream out = null;try { // 调用工具类的createDoc方法生成Word文档 file = createDoc(map,freemarkerTemplate); fin = new FileInputStream(file); String fileName = title + ".doc";/*response.setCharacterEncoding("utf-8"); response.setContentType("application/msword"); // 设置浏览器以下载的方式处理该文件名 String fileName = title + ".doc"; response.setHeader("Content-Disposition", "attachment;filename=" .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8")))); out = response.getOutputStream(); */ File file2 = new File(flieUrl+fileName);if (!file2.exists()) {file2.createNewFile();}out = new FileOutputStream(file2);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(); // 删除临时文件 } } private static File createDoc(Map<?, ?> dataMap, Template template) { String name = "sellPlan.doc"; File f = new File(name); Template t = template; try { // 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开 Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8"); t.process(dataMap, w); w.close(); } catch (Exception ex) { ex.printStackTrace(); throw new RuntimeException(ex); } return f; }
}
2、将图片转为base64的类,这里我因为需求问题做了等比例缩放功能,缩放到多大由double widthMax = 350; double heightMax = 350;这两个值确定,这个大小基本就是你存图片表格的大小。
package com.gykj.utils;import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;import javax.imageio.ImageIO;import sun.misc.BASE64Encoder;public class getImageBase {//获得图片的base64码//@SuppressWarnings("deprecation")public static Map<String, Object> getBase64(String imgsrc) {
// if(src==null||src==""){
// return "";
// }
// File file = new File(src);
// if(!file.exists()) {
// return "";
// }
// InputStream in = null;
// byte[] data = null;
// try {
// in = new FileInputStream(file);
// } catch (FileNotFoundException e1) {
// e1.printStackTrace();
// }
// try {
// data = new byte[in.available()];
// in.read(data);
// in.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// BASE64Encoder encoder = new BASE64Encoder();
// return encoder.encode(data);Map<String, Object> map = new HashMap<String, Object>();try {double rate = 0;int widthdist = 0;int heightdist = 0;double widthMax = 350;double heightMax = 350;File srcfile = new File(imgsrc);// 检查图片文件是否存在if (!srcfile.exists()) {System.out.println("文件不存在");}// 如果比例不为空则说明是按比例压缩//获得源图片的宽高存入数组中int[] results = getImgWidthHeight(srcfile);//计算具体等比比例int width = results[0];int height = results[1];double widthRate = widthMax/width;double heightRate = heightMax/height;if (heightRate > widthRate) {rate = widthRate;}else {rate = heightRate;}if (results == null || results[0] == 0 || results[1] == 0) {return null;} else {//按比例缩放或扩大图片大小,将浮点型转为整型widthdist = (int) (results[0] * rate);heightdist = (int) (results[1] * rate);}// 开始读取文件并进行压缩Image src = ImageIO.read(srcfile); // 构造一个类型为预定义图像类型之一的 BufferedImageBufferedImage tag = new BufferedImage((int) widthdist, (int) heightdist, BufferedImage.TYPE_INT_RGB);//绘制图像 getScaledInstance表示创建此图像的缩放版本,返回一个新的缩放版本Image,按指定的width,height呈现图像//Image.SCALE_SMOOTH,选择图像平滑度比缩放速度具有更高优先级的图像缩放算法。tag.getGraphics().drawImage(src.getScaledInstance(widthdist, heightdist, Image.SCALE_SMOOTH), 0, 0, null);// //创建文件输出流
// FileOutputStream out = new FileOutputStream(imgdist);
// //将图片按JPEG压缩,保存到out中
// JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
// encoder.encode(tag);
// //关闭文件输出流
// out.close();ByteArrayOutputStream baos = new ByteArrayOutputStream();ImageIO.write(tag, "jpg", baos);byte[] bytes = baos.toByteArray();BASE64Encoder encoder = new BASE64Encoder();map.put("img", encoder.encodeBuffer(bytes).trim());map.put("w", widthdist);map.put("h", heightdist);map.put("name", imgsrc);baos.close();return map;} catch (Exception ef) {ef.printStackTrace();}return null;}/*** 获取图片宽度和高度* * @param 图片路径* @return 返回图片的宽度*/public static int[] getImgWidthHeight(File file) {InputStream is = null;BufferedImage src = null;int result[] = { 0, 0 };try {// 获得文件输入流is = new FileInputStream(file);// 从流里将图片写入缓冲图片区src = ImageIO.read(is);result[0] =src.getWidth(null); // 得到源图片宽result[1] =src.getHeight(null);// 得到源图片高is.close(); //关闭输入流} catch (Exception ef) {ef.printStackTrace();}return result;}}
3、最后的值测试样例类以及测试模板,因此篇幅原因不上来了,如需要,留言邮箱即可
4、需要demo的人可能有点多,我没法及时回复,就放百度云盘了:
https://pan.baidu.com/s/1fgjJBIbtzXJNnCwLlI7F0Q
提取码:u6qq
java后台导出word,详细过程及趟过的坑相关推荐
- java后台导出excel代码详细讲解,java基础面试笔试题
我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码或搜索下图红色VX号,加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起 ...
- 【Java实现导出Word文档功能 XDocReport +FreeMarker】
Java实现导出Word文档功能(XDocReport +FreeMarker) 前言 在日常的开发工作中,我们时常会遇到导出Word文档报表的需求,比如公司的财务报表.医院的患者统计报表.电商平台的 ...
- java freemark导出word (模板、单张图片、多张图片源码)
java freemark导出word (模板.单张图片.多张图片源码) 首先模板设置 代码编辑 设置word只读.全部 环境设置.jar包 模板设置 ** 设置模板然后另存为 这里一定要选择对xml ...
- java循环导出word文档_Java使用freemarker导出word文档
通过freemarker,以及JAVA,导出word文档. 共分为三步: 第一步:创建模板文件 第二步:通过JAVA创建返回值. 第三步:执行 分别介绍如下: 第一步: 首先创建word文档,按照想要 ...
- java 中导出word后压缩文件_Java批量导出word压缩后的zip文件案例
一.js代码,由于参数比较大所以利用form表单使用post导出 function export_word(){ var selectedRows = $("#dg").datag ...
- java freemarker导出word 带图片,文件导出后图片无法使用office正常打开
问题记录:java freemark导出word 带图片,文件导出后图片无法使用office正常打开,解决之! 现象:wps打开正常,office如下 修改点: 图片${image1}标签前后保证无空 ...
- Java poi导出word文件
Java在导出word文件时主要对表格中内容垂直居中处理做以记录方便后续碰到类似问题解决. maven pom.xml中添加poi依赖 <!-- word.excel工具 --> < ...
- java后台导出Excel表格
引言 java后台导出表格一般分两种:注解配置(@Excel)导出和自定义导出 注解配置(@Excel)导出 添加poi依赖 <dependency><groupId>cn.a ...
- Java后台代码word转pdf文件下载(类库参考)附jar包
word文件中需要转为pdf文件,word中的空格部分被程序后台动态填充,例如: 上图中带有[标]字样的位置,可以被替换为动态数据,最后被导出为pdf文件. 贴一下java工具类代码 所有过程按照下面 ...
最新文章
- 软件开发过程中遇到的问题
- 什么是 JSON ?
- 坚持,这两个字非常重要!
- Homogeneous Coordinates(齐次坐标)
- python数组删除最后一个元素_删除numpy数组中的最后一个元素
- 从400+节点ElasticSearch集群的运维中,我们总结了这些经验
- but was actually of type [com.sun.proxy.$Proxy13]
- macos 字体_巧用 iTerm2 zsh oh-my-zsh 打造炫酷的 MacOS 终端环境
- 微信硬件平台智能家居行业解决方案
- sqoop导入/导出
- R语言处理时间序列数据
- 开关量采集模块支持PWM占空比输出无线数据采集模块Modbus TCP协议
- 如何看误差累积分布图
- 《Adobe After Effects CS6完全剖析》——第2章 时间标尺 营造整洁的工作流程之梦...
- bzoj5185 [Usaco2018 Jan]Lifeguards
- 【QT】翻金币小游戏·我的学习版
- Python解包技巧
- 软件测试 - 测试基础知识
- 2013新春奉送 Android摄像头开发完美demo--- 循环聚焦 缩放大小 旋转picture 查询支持的pict
- 鱼眼图像转换为全景图像_使用A框架创建3D全景图像