根据word模板定制报表

踩过的坑

在网上看到不少关于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);
				// 只使用 name
				zipOutputStream.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);
					}
				}
			}
			// 关闭此 entry
			zipInputStream.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);
		}
	}
}

	@Test
	public 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 );
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值