踩过的坑

在网上看到不少关于word模板生成报表的例子,踩了不少的坑。最大的问题就是生成的word文档在电脑打开显示正常,而用手机打开显示的是全是xml标签。

本质

docx格式的文档本质上是一个ZIP文件。docx 格式文件的主要内容是保存为XML格式的,但文件并非直接保存于磁盘。它是保存在一个ZIP文件中,然后取扩展名为docx。将.docx 格式的文件后缀改为ZIP后解压, 可以看到解压出来的文件夹中有word这样一个文件夹,它包含了Word文档的大部分内容。而其中的document.xml文件则包含了文档的主要文本内容。

步骤

第一步【新建文档】

新建一个test.docx文档,内容如下。

第二步【zip目录文件说明】

把test.docx的文档后缀改为zip。打开压缩文件我们主要进入word目录下,结构如下。
media:存放word文档里面插入的图片。
theme:主题样式。
document.xml:文档主要内容。
header1.xml:文档页眉内容。
footer1.xml:文档页脚内容。

第三步【修改xml文件】

例如我现在需要对文档主体内容进行修改。把document.xml文件内容剪切,进行代码格式化,再把格式化的内容粘贴回去。
把我们的中文替换成FTL标签,参考如下链接。
https://blog.csdn.net/asa_prince/article/details/82018446
https://blog.csdn.net/asa_prince/article/details/82017976
https://blog.csdn.net/qq_33616529/article/details/78291103
https://blog.csdn.net/jayainuo/article/details/69220728
https://www.cnblogs.com/zhaoYuQing-java2015/p/6046697.html
例如表格可以删除掉李四这一行的内容,把张三这一行的内容循环遍历<#list userList! as user> </#list>,再把张三替换成${!user.name},其他属性类似操作。

第四步【执行代码】

  1. 生成word报表相关代码
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import freemarker.template.Configuration;
import freemarker.template.Template;public class WordUtils {private static Logger LOGGER = LoggerFactory.getLogger(WordUtils.class);/*** 往xml文件填充数据* @param dataMap 数据* @param templatePath 模板路径* @param templateName 模板名称* @param generatePath 生成的文件路径*/public static void process(Map<String,Object> dataMap,String templatePath,String templateName, String generatePath) {Writer out = null;try {Configuration configuration = new Configuration(Configuration.getVersion());configuration.setDefaultEncoding("utf-8");configuration.setDirectoryForTemplateLoading(new File(templatePath));Template t = configuration.getTemplate(templateName, "utf-8");out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(generatePath)), "utf-8"));t.process(dataMap, out);} catch (Exception e) {LOGGER.error("往xml文件填充数据失败:{}",e.getMessage());} finally {close(out);}}/*** 替换某个 item,* @param zipInputStreamPath  zip文件的zip输入流路径* @param zipOutputStreamPath 输出的zip输出流路径* @param itemInputStreamMap 要替换的 item 的内容输入流路径*/public static void replaceItem(String zipInputStreamPath, String zipOutputStreamPath, Map<String,String> itemInputStreamMap) {ZipInputStream zipInputStream = null;ZipOutputStream zipOutputStream = null;try {zipInputStream = new ZipInputStream(new FileInputStream(new File(zipInputStreamPath)));zipOutputStream = new ZipOutputStream(new FileOutputStream(new File(zipOutputStreamPath)));ZipEntry entryIn;int len = -1;// 缓冲区byte[] buf = new byte[8 * 1024];while ((entryIn = zipInputStream.getNextEntry()) != null) {String entryName = entryIn.getName();ZipEntry entryOut = new ZipEntry(entryName);// 只使用 namezipOutputStream.putNextEntry(entryOut);if(itemInputStreamMap.containsKey(entryName)) {String path = itemInputStreamMap.get(entryName);InputStream inputStream = new FileInputStream(new File(path));// 使用替换流while ((len = (inputStream.read(buf))) > 0) {zipOutputStream.write(buf, 0, len);}close(inputStream);itemInputStreamMap.remove(entryName);}else {// 输出普通Zip流while ((len = (zipInputStream.read(buf))) > 0) {zipOutputStream.write(buf, 0, len);}}}// 关闭此 entryzipInputStream.closeEntry();zipOutputStream.closeEntry();} catch (IOException e) {LOGGER.error("替换item失败:{}",e.getMessage());} finally {close(zipInputStream);close(zipOutputStream);}}private static void close(InputStream inputStream) {if (null != inputStream) {try {inputStream.close();} catch (IOException e) {LOGGER.error(e.getMessage());}}}private static void close(OutputStream outputStream) {if (null != outputStream) {try {outputStream.flush();outputStream.close();} catch (IOException e) {LOGGER.error(e.getMessage());}}}private static void close(Writer out) {if (null != out) {try {out.flush();out.close();} catch (IOException e) {LOGGER.error(e.getMessage());}}}
}
  1. 后台生成统计图表图片相关代码,需要安装phantomjs,引入相关的js。[无需生成统计图表可以忽略此代码]
import java.io.File;
import java.io.FileOutputStream;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class EchartsUtils {private static final Logger LOG = LoggerFactory.getLogger(EchartsUtils.class);public static final String FILETYPE = ".js";public static final String IMGTYPE = ".png";public static final float SCALE = 0.25f;public static final int WIDTH = 800;public static final int HEIGHT = 400;/*** 生成echarts图片* @param json * @param rootPath    生成的临时文件夹目录* @param name        图片名称* @return* @throws Exception*/public static boolean getEchartsImg(String json,String upgradePath,String rootPath,String name){boolean flag = true;Process process = null;try {rootPath += name; //生成js文件writeEchartsJs(json,rootPath+FILETYPE);//生成echarts图片Runtime rt = Runtime.getRuntime();process = rt.exec("phantomjs " + upgradePath + File.separator + ReportConst.PATH_ECHARTS + "echarts-convert.js -infile " + rootPath + FILETYPE + " -outfile " + rootPath + IMGTYPE + " -scale " + SCALE + " -width " + WIDTH + " -height" + HEIGHT);//等待图片生成process.waitFor();} catch (Exception e) {flag = false;LOG.error("生成EchartsIMG失败:{}",e.getMessage());}finally {if(process != null) {process.destroy();}}return flag;}public static void writeEchartsJs(String json, String path) {FileOutputStream outSTr = null;try {File file = new File(path);outSTr = new FileOutputStream(file);outSTr.write(json.getBytes());} catch (Exception e) {LOG.error("生成EchartsJS文件失败:{}",e.getMessage());} finally {FileUtils.close(outSTr);}}
}
 @Testpublic void generateWord() {Map<String, Object> dataMap = new HashMap<String, Object>();dataMap.put("title", "XXX报表");List<Map<String, Object>> list = new ArrayList<>();for (int i = 1; i <= 50; i++) {Map<String, Object> map = new HashMap<String, Object>();dataMap.put("name", "张三"+i);dataMap.put("sex", "男");dataMap.put("age", i);list.add(map);}dataMap.put("userList", list);String json = getPieOption();boolean flag = EchartsUtils.getEchartsImg(json, "",IMGURL, "test_pie");if(flag) {WordUtils.process(dataMap, PATH, "test_document.xml", PATH+"data.xml");Map<String, String> itemInputStreamMap = new HashMap<>();itemInputStreamMap.put("word/document.xml", PATH+"data.xml");itemInputStreamMap.put("word/media/image1.png", IMGURL+"test_pie.png");WordUtils.replaceItem(PATH+"test.zip", PATH+"test_generate.docx", itemInputStreamMap );}}

根据word模板定制报表相关推荐

  1. POI 3.9根据word模板生成报表

    XWPFDocument对象 POI是apache提供的可以操作word文档的第三方jar.POI能操作word是使用XWPFDocument对象. XWPFDocument对象可以解析docx文件, ...

  2. word模板生成word报表文档

    主要功能为根据word模板生成word报表文档,注意引用Interop.Word.dll; 首先要生成word程序对象 Word.Application app = new Word.Applicat ...

  3. C#根据word模板生成word表格报表文档

    主要功能为根据word模板生成word报表文档,注意引用Interop.Word.dll; 首先要生成word程序对象 Word.Application app = new Word.Applicat ...

  4. apache poi使用例_使用java Apache poi 根据word模板生成word报表例子

    [实例简介] 使用java Apache poi 根据word模板生成word报表 仅支持docx格式的word文件,大概是word2010及以后版本,doc格式不支持. 使用说明:https://b ...

  5. POI利用word模板动态生成word报表以及动态生成word表格

    目录 核心依赖 动态表格 测试类 工具类 动态数据 测试类 工具类 核心依赖 <dependency><groupId>org.apache.poi</groupId&g ...

  6. 使用java Apache poi 根据word模板生成word报表

    使用java Apache poi 根据word模板生成word报表 使用poi读取word模板,替换word中的{text}标签,并根据自定义标签循环生成表格或表格中的行. 代码示例下载:https ...

  7. java使用Apache poi根据word模板生成word报表(增加插入符号、控制分页功能)

    原文链接:https://blog.csdn.net/u012775558/article/details/79678701 根据原代码新增了插入符号和控制分页功能.改了类名,一些方法,新增一个符号类 ...

  8. 【QT】word文档操作实例——根据word模板生成word报表

    文章目录 引言 一.word模板准备 二.WordDemo实现 1.mainwindow.ui 2.mainwindow.h 3.mainwindow.cpp 三.实现效果 引言 在QT5.3中,在. ...

  9. 基于Easypoi+jfree,使用SpringBoot架构,Java编程实现word模板导出Word报表

    目录 1.项目目录结构 2.pom.xml添加的依赖 3.编写jfreeutil工具类 4.编写wordutil工具类 5.编写word模板 7.运行效果 8.复杂布局实现 8.1如何实现图片并排 S ...

最新文章

  1. MegEngine推理性能优化
  2. SAP ABAP BAPI_GOODSMVT_CREATE的几个应用
  3. Android studio 4.1 不显示光标当前的类名、方法名
  4. WPF DataGrid横向显示
  5. android linearllayout 隐藏 动画,AnimatedLinearLayout:带删除动画的LinearLayout
  6. 电机控制pid_微电机控制如此简单,揭秘微电机调速的控制,PID控制之双环调速...
  7. 2015 03 03 复习 上课笔记(一)
  8. 【高数+AI】中山大学的学霸小哥开源了一个能帮你做高数题的AI
  9. java数列的个位数求和_java二位数组相加
  10. 转载 java序列化与反序列化
  11. 在Android中Unity3D透明背景的实现
  12. ROS开发--Qt接收摇杆话题
  13. matlab矩阵里面星号,矩阵中出现加号和星号 什么意思
  14. Windows的sc命令详解
  15. 蓝桥 卷“兔”来袭编程竞赛专场-07明码加密 题解
  16. 成功解决ValueError: Duplicate plugins for name projector
  17. JavaScript中Set的使用
  18. ProcessOn在线画图
  19. std::thread vs CreateThread
  20. 小米红米Note4X(高配版)解BL锁教程申请BootLoader解锁教程

热门文章

  1. wep加密破解原理简述 实战
  2. nginx入门、了解
  3. linux提取ttf字体轮廓,[TTF字体]提取TTF字体的轮廓(二)
  4. Excel之使用表格
  5. 计算机网络运动会 口号,运动会口号大全_运动会口号荟萃
  6. 关于计算机的英语听力,练习英语听力的方法有哪些
  7. 有关神经网络的训练算法,神经网络训练结果分析
  8. Python实现朴素贝叶斯分类器
  9. bazel 学习笔记
  10. c语言实现购买火车票系统