700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 导出word文档生成docx格式 包含freemarker遍历多张图片

导出word文档生成docx格式 包含freemarker遍历多张图片

时间:2019-03-23 04:15:48

相关推荐

导出word文档生成docx格式 包含freemarker遍历多张图片

为了导出docx格式看了等多文档,最后做个总结依赖包用到dom4j和freemarker,最为方便。

<!-- /artifact/freemarker/freemarker --><dependency><groupId>freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.9</version></dependency><!-- /artifact/dom4j/dom4j --><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency>

0.主要目的:将这样一个页面导出为word文档为doc格式,包含一些文本和循环遍历出来的echarts图表。

1.新建一个word文档(docx格式或doc),生成模板内容,例如下面这种。

整体思路

-保存后,复制出来一份,

-修改后缀名为zip。

-解压到一个文件夹中。

-打开文件夹看到如下目录

-获取word里的document.xml文档以及_rels文件夹下的document.xml.rels文档

-把内容填充到document.xml里,以及图片配置信息填充至document.xml.rels文档里

-在输入docx文档的时候把填充过内容的的 document.xml、document.xml.rels用流的方式写入zip(详见下面代码)。

-把图片写入zip文件下word/media文件夹中

-输出docx文档(因为word文档本身就是ZIP格式实现的)

2.目录结构如下:主要文件由上一步拷贝过来的

document.xml里存放主要数据media存放图片信息_rels里存放配置信息

document.xml中存放图片的模板主要内容

3.document.xml修改模板内容加上freemarker遍历map集合,填入数据

4.document.xml.rels修改模板引用内容

注意:这里图片配置信息是根据 rId来获取的。docx模板总的${mdl.rId}就是rId的具体值。

为了避免重复,我的图片rId从12开始(在我没有修改之前,里面最大的rId是rId12)。

5.header1.xml页眉 (可不要)

6.[Content_Types].xml文件模板

7.前端页面关键就是请求,根据自己需求构建json字符串格式,map数据传到后台

//导出word docxfunction download_reportNew() {//此处遍历页面得数据,放到json中,可根据自己需要省略-----------------startconsole.log("new wordx");var title= '${reportData.title}';var reportUnit= '${reportData.reportUnit!}';var reportTypeDate= '${reportData.reportTypeDate!}';var json;var jsonHead = {"title": title, "reportUnit": reportUnit,"reportTypeDate":reportTypeDate};//此处用到了freemarker的模板遍历数据<#list reportData.reportModels as model>var modelTitle_${model ? index}= '${model.title!}';var modelDataSource_${model ? index}= '${model.dataSource!}';var modelShowContent_${model ? index}= '${model.showContent!}';var model_pic_${model ? index} = null ;var pic_${model ? index} = null ;if (model_${model ? index}.option != null) {pic_${model ? index}=model_${model ? index}.chart.getConnectedDataURL();model_pic_${model ? index}=pic_${model ? index}.substr(22,pic_${model ? index}.length);}var jsonBody = {"model_${model ? index}":{"modelTitle": modelTitle_${model ? index},"modelDataSource": modelDataSource_${model ? index},"modelShowContent":modelShowContent_${model ? index},"model_pic":model_pic_${model ? index}}};//最后的json对象json=$.extend(true,jsonHead,jsonBody);</#list>//此处遍历页面得数据,放到json中,可根据自己需要省略-----------------end$.ajax({//第一次请求生成doc临时文件url: '${base}/report/reportView/reportExportNew.do',method: 'POST',contentType: 'application/json;charset=utf-8',data: JSON.stringify(json),success: function (data) {if (data.status == 0) {//第二次请求读取文件写入response输出流,实现下载。window.location.href = '${base}/report/reportView/reportExportLast.do'+ "?filepath=" + data.retinfo ;} else {alert("下载word失败!");}},error: function (data) {alert('文件下载失败' + data);}})}

8.第一次请求生成doc临时文件:word导出为doc格式的后台controller类

/*** @param* @Description 报表导出* @Date /11/20 11:53* @Param map 填入模板的数据* @Author */@PostMapping("/reportExportNew")public void reportExportNew(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String,Object> map) throws IOException {WebResult res = new WebResult();try {String lastFilePath = reportViewService.createWordDocx(map);String lastPath = lastFilePath.replace(SEPARATOR,"~");String text=filastPath;//生成的文件名放到response中返回(此处根据自己需要可直接返回text) ---------startPrintWriter out = null;try {response.setContentType("application/json;charset=UTF-8");out = response.getWriter();out.write(text);} catch (IOException var9) {LOGGER.error(var9.getMessage(), var9);} finally {if (out != null) {out.print("");out.close();}}//生成的文件名放到response中返回(此处根据自己需要可直接返回text) ---------end} catch (Exception e) {e.printStackTrace();}}

9.后台构建map填入模板需要的数据createWordDocx(map)方法

/*** 创建docx 返回临时路径* @param map* @return* @throws IOException*/public String createWordDocx(Map<String, Object> map) throws IOException{/*** @param dataMap参数数据* @param docxTemplateFiledocx模主板名称* @param xmlDocument docx中document.xml模板文件 用来存在word文档的主要数据信息* @param xmlDocumentXmlRels docx中document.xml.rels 模板文件 用来存在word文档的主要数据配置 包括图片的指向* @param xmlContentTypes docx中 [Content_Types].xml 模板文件 用来配置 docx文档中所插入图片的类型 如 png、jpeg、jpg等* @param xmlHeader docx中 header1.xml 模板文件 用来配置docx文档的页眉文件* @param templatePath模板存放路径 如 /templates/* @param outputFileTempPath 所生成的docx文件的临时路径文件夹 如果 temp/0914051811/* @param outputFileName 所生成的docx文件名称 如 xxx.docx 或 xxx.doc* */String timeStr = LocalDateUtils.getCurrentTime_yyyyMMddHHmmssSSS();String docxTemplateFile = "docxTemplates.docx";String xmlDocument = "document.xml";String xmlDocumentXmlRels = "document.xml.rels";String xmlContentTypes = "[Content_Types].xml";//可以用来修改页眉的一些信息String xmlHeader = "header1.xml";String templatePath = SEPARATOR + "template" + SEPARATOR;String outputFileTempPath = templatePath+"temp" + SEPARATOR + timeStr + SEPARATOR;String outputFileName = timeStr + "."+SUFFIX_DOCX;String classPath=ReportViewServiceImpl.class.getResource("/").getPath().toString();LOGGER.info("classPath:{}",classPath);LOGGER.info("templatePath:{}",templatePath);LOGGER.info("outputFileTempPath:{}",outputFileTempPath);LOGGER.info("outputFileName:{}",outputFileName);//填充整体数据Map<String, Object> dataMap = new HashMap<>(16);//模块内容列表List<Map<String, Object>> modelList = new ArrayList<>(16);//单个模块Map<String, Object> model;// 页眉dataMap.put("ymdhis", LocalDateUtils.getCurrentTime_yyyyMMddHHmmss());//图片类型List<String> modelTypes = new ArrayList<>();modelTypes.add("png");dataMap.put("mdlTypes", modelTypes);//取空白图片Base64码String url = classPath+"template/blank.png";String blankEncode = getImageStr(url);// 文档标题dataMap.put("title", map.get("title"));dataMap.put("reportUnit", map.get("reportUnit"));dataMap.put("reportTypeDate", map.get("reportTypeDate"));//模块数量int modelNum = map.size() - dataMap.size()+2;for (int i = 0; i < modelNum; i++) {model = (Map<String, Object>) map.get("model_" + i);if (model.get("model_pic") == null) {//64位编码格式改成path和namemodel.put("model_pic", blankEncode);}//每个文件路径String fileName = "pic"+i+".png";String filePath = classPath+outputFileTempPath+fileName;model.put("path",filePath);model.put("name",fileName);modelList.add(model);}//批量生成文件for (Map<String, Object> mod:modelList) {baseToFile(mod);}dataMap.put("modelList", modelList);try {String lastFilePath = WordUtils.createDocx(dataMap, docxTemplateFile, xmlDocument, xmlDocumentXmlRels, xmlContentTypes,xmlHeader, templatePath, outputFileTempPath, outputFileName);return lastFilePath;} catch (Exception e) {e.printStackTrace();}return null;}

10.wordUtiles类的createDocx

import org.dom4j.Document;import org.dom4j.DocumentHelper;import org.dom4j.Element;import java.io.*;import .URL;import java.util.*;import java.util.zip.ZipEntry;import java.util.zip.ZipFile;import java.util.zip.ZipOutputStream;/*** @Description docx、doc文档生成工具类 (改变后缀名即可)* 在使用制作模板的过程中如果模板中有图片那就保留图片,注意[Content_Types].xml和document.xml.rels文档* 如果模板中没有图片 则不需要设置[Content_Types].xml和document.xml.rels* 由于word模板的个性化 所以 每次做模板都要重新覆盖原来的模板* @Author */public class WordUtils {private final static String SEPARATOR = File.separator;/*** @param dataMap 参数数据* @param docxTemplateFile docx模主板名称* @param xmlDocument docx中document.xml模板文件 用来存在word文档的主要数据信息* @param xmlDocumentXmlRels docx中document.xml.rels 模板文件 用来存在word文档的主要数据配置 包括图片的指向* @param xmlContentTypes docx中 [Content_Types].xml 模板文件 用来配置 docx文档中所插入图片的类型 如 png、jpeg、jpg等* @param xmlHeaderdocx中 header1.xml 模板文件 用来配置docx文档的页眉文件* @param templatePath 模板存放路径 如 /templates/* @param outputFileTempPath 所生成的docx文件的临时路径文件夹 如果 temp/0914051811/* @param outputFileName所生成的docx文件名称 如 xxx.docx 或 xxx.doc*/public static String createDocx(Map dataMap, String docxTemplateFile, String xmlDocument, String xmlDocumentXmlRels,String xmlContentTypes, String xmlHeader, String templatePath,String outputFileTempPath, String outputFileName) throws Exception {URL basePath = WordUtils.class.getClassLoader().getResource("");String realTemplatePath = basePath.getPath() + templatePath;//临时文件产出的路径String outputPath = basePath.getPath() + outputFileTempPath;String lastFilePath = outputFileTempPath+outputFileName;List<String> delFileList = new ArrayList<>();try {//获取 document.xml.rels 输入流String xmlDocumentXmlRelsComment = FreeMarkUtils.getFreemarkerContent(dataMap, xmlDocumentXmlRels, templatePath);ByteArrayInputStream documentXmlRelsInput = new ByteArrayInputStream(xmlDocumentXmlRelsComment.getBytes());//获取 header1.xml 输入流ByteArrayInputStream headerInput = FreeMarkUtils.getFreemarkerContentInputStream(dataMap, xmlHeader, templatePath);//获取 [Content_Types].xml 输入流ByteArrayInputStream contentTypesInput = FreeMarkUtils.getFreemarkerContentInputStream(dataMap, xmlContentTypes, templatePath);//读取 document.xml.rels 文件 并获取rId 与 图片的关系 (如果没有图片 此文件不用编辑直接读取就行了)Document document = DocumentHelper.parseText(xmlDocumentXmlRelsComment);// 获取根节点Element rootElt = document.getRootElement();// 获取根节点下的子节点headIterator iter = rootElt.elementIterator();List<Map<String, String>> picList = (List<Map<String, String>>) dataMap.get("modelList");// 遍历Relationships节点while (iter.hasNext()) {Element recordEle = (Element) iter.next();String id = recordEle.attribute("Id").getData().toString();String target = recordEle.attribute("Target").getData().toString();if (target.indexOf("media") == 0) {for (Map<String, String> picMap : picList) {if (target.endsWith(picMap.get("name"))) {picMap.put("rId", id);}}}}//覆盖原来的picList;dataMap.put("modelList", picList);//获取 document.xml 输入流ByteArrayInputStream documentInput = FreeMarkUtils.getFreemarkerContentInputStream(dataMap, xmlDocument, templatePath);File docxFile = new File(realTemplatePath + SEPARATOR + docxTemplateFile);if (!docxFile.exists()) {docxFile.createNewFile();}ZipFile zipFile = new ZipFile(docxFile);Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();File tempPath = new File(outputPath);//如果输出目标文件夹不存在,则创建if (!tempPath.exists()) {tempPath.mkdirs();}ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(outputPath + outputFileName));//覆盖文档int len = -1;byte[] buffer = new byte[1024];while (zipEntrys.hasMoreElements()) {ZipEntry next = zipEntrys.nextElement();InputStream is = zipFile.getInputStream(next);if (next.toString().indexOf("media") < 0) {// 把输入流的文件传到输出流中 如果是word/document.xml由我们输入zipout.putNextEntry(new ZipEntry(next.getName()));//写入图片配置类型if ("[Content_Types].xml".equals(next.getName())) {if (contentTypesInput != null) {while ((len = contentTypesInput.read(buffer)) != -1) {zipout.write(buffer, 0, len);}contentTypesInput.close();}} else if (next.getName().indexOf("document.xml.rels") > 0) {//写入填充数据后的主数据配置信息if (documentXmlRelsInput != null) {while ((len = documentXmlRelsInput.read(buffer)) != -1) {zipout.write(buffer, 0, len);}documentXmlRelsInput.close();}} else if ("word/document.xml".equals(next.getName())) {//写入填充数据后的主数据信息if (documentInput != null) {while ((len = documentInput.read(buffer)) != -1) {zipout.write(buffer, 0, len);}documentInput.close();}} else if ("word/header1.xml".equals(next.getName())) {//写入填充数据后的页眉信息if (headerInput != null) {while ((len = headerInput.read(buffer)) != -1) {zipout.write(buffer, 0, len);}headerInput.close();}} else {while ((len = is.read(buffer)) != -1) {zipout.write(buffer, 0, len);}is.close();}}}//覆盖文档//写入新图片len = -1;if (picList != null && !picList.isEmpty()) {for (Map<String, String> pic : picList) {ZipEntry next = new ZipEntry("word" + SEPARATOR + "media" + SEPARATOR + pic.get("name"));zipout.putNextEntry(new ZipEntry(next.toString()));InputStream in = new FileInputStream(pic.get("path"));while ((len = in.read(buffer)) != -1) {zipout.write(buffer, 0, len);}in.close();}}zipout.close();return lastFilePath;} catch (Exception e) {e.printStackTrace();throw new Exception("生成docx文件失败!");}}/*** 递归删除文件夹** @param dir*/public static void delFiles(String dir) {try {File file = new File(dir);if(!file.exists()){return;}if(file.isFile() || file.list()==null) {file.delete();System.out.println("删除了"+file.getName());}else {File[] files = file.listFiles();for(File a:files) {a.delete();}file.delete();System.out.println("删除了"+file.getName());}} catch (Exception e) {e.printStackTrace();}}}

11.第二次请求读取文件写入response输出流,实现下载。:将文件输出到response中,浏览器实现下载

/*** @param* @Description 报表导出* @Date /11/13 11:53* @Author */@RequestMapping("/reportExportLast")public void reportExportLast(HttpServletRequest request, HttpServletResponse response) throws IOException {String templateName = request.getParameter("filepath");if (templateName.isEmpty()) {templateName = "report" + System.currentTimeMillis();}String classPath=ReportViewAction.class.getResource("/").getPath().toString();String fileName = templateName.replace("~",SEPARATOR);String filePath = classPath+fileName;String name = "";//文件后缀名String fileExt = fileName.substring(fileName.lastIndexOf(".")+1);File file = new File(filePath);try (InputStream inputStream = new FileInputStream(file);ServletOutputStream out = response.getOutputStream()) {if (SUFFIX_DOCX.equals(fileExt)){name = new String("大数据报告.docx".getBytes("UTF-8"),"UTF-8");response.setContentType("application/msword;charset=UTF-8");}else if (SUFFIX_PDF.equals(fileExt)){name = new String("大数据报告.pdf".getBytes("UTF-8"),"UTF-8");response.setContentType("application/pdf;charset=UTF-8");}name = URLEncoder.encode(name,"UTF-8");response.setHeader("Content-Disposition", "attachment;filename=" + name);byte[] buffer = new byte[1024];int bytesToRead;while ((bytesToRead = inputStream.read(buffer)) != -1) {out.write(buffer, 0, bytesToRead);}} catch (IOException e) {e.printStackTrace();} finally {//删除临时文件String outputPath = filePath.substring(0,filePath.lastIndexOf(SEPARATOR)+1);WordUtils.delFiles(outputPath);}}

具体代码

/zc0709/JavaUtilsProject

主要工具类

/zc0709/JavaUtilsProject/blob/master/src/main/java/com/sl/utils/office/word/WordUtils.java

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。