一、jar包支持

1、freemarker

freemarker-2.3.28.jar

2、poi

poi-3.9.jar
poi-examples-3.9.jar
poi-excelant-3.9.jar
poi-ooxml-3.9.jar
poi-ooxml-schemas-3.9.jar
poi-scratchpad-3.9.jar

ps:如果项目里poi版本为poi-4.1.0及以上,建议使用poi-tl-1.5.0

  附带地址教程http://deepoove.com/poi-tl/

二、大概步骤

1、创建word—调整样式—调整XML代码—修改扩展名ftl,就可以使用了

我用的office版本是  office专业增强版 2016,不同版本可能有所不同。

三、创建office2007的重要步骤

wps没试过,不清楚。就是讨厌wps,没有原因。

1、表格取值

1、先用office2007新建一个word文档(一定要!!!),office是向下兼容的,2007及以下版本,无法打开freemarker导出后的2007以上版本的(意思是2007无法打开高版本)

2、新建好07.docx之后,用高版本或者当前版本打开。

  设定你需要的模板,先把内容都填充了,先调整好样式,在删掉内容后,写好参数 ,后台是以map key的形式往模板里插入的${xxx},尽量不要有个空格。

  需要插入图片的地方插入两张...最好是不同的图片,可以看出来区别(方便修改代码,因为要看懂word的XML语言,也后面多个图片循环),列表同样。见下图

3、表格完成之后,另保存为.xml文件,(是另存为,不是直接修改扩展名),选择Word XML 文档就行,不用2003 XML的。

4、另存为.XML文件,就可以用Notpad++打开看代码了,如果遇到  ${xxx}  这几个占位符不在一起的情况,都把他们调整放在一起,转xml的时候格式造成的

譬如这种:直接修改,红圈的样式可以直接删掉,不影响。再看看其他的,有很多占位符有问题。

2、图片循环

1、找到有一大串这种字符的地方(Base64字符串),word图片转换过的。直接删掉就可以,我们是要后台传值过来的。

2、然后修改这段代码,循环图片,自己缕缕,就能看出来,跟jsp列表循环差不多

如果用的2016版本,就需要循环三个地方。有指向性的问题,七标题里面会提到

方便使用,这里粘贴这部分代码。

 <!-- 代表一行几列的意思,由图片宽度决定 --><w:pict><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><v:stroke joinstyle="miter"/><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"/><v:f eqn="sum @0 1 0"/><v:f eqn="sum 0 0 @1"/><v:f eqn="prod @2 1 2"/><v:f eqn="prod @3 21600 pixelWidth"/><v:f eqn="prod @3 21600 pixelHeight"/><v:f eqn="sum @0 0 1"/><v:f eqn="prod @6 1 2"/><v:f eqn="prod @7 21600 pixelWidth"/><v:f eqn="sum @8 21600 0"/><v:f eqn="prod @7 21600 pixelHeight"/><v:f eqn="sum @10 21600 0"/></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/><o:lock v:ext="edit" aspectratio="t"/></v:shapetype><!-- office2007 图片循环--><#list image as item ><w:binData w:name="wordml://${item.image_name}" xml:space="preserve">${item.image_base64}</w:binData><v:shape id="_x0000_i1027" type="#_x0000_t75" style="width:255pt;height:255pt"><v:imagedata src="wordml://${item.image_name}" o:title="photo"/><o:lock v:ext="edit" aspectratio="f"/></v:shape></#list></w:pict>

3、列表循环

1、列表循环同理,找到大致的列表,写个list循环就可以,这里只截图我的结果了

好了,这部分完事了

四、jsp、js代码

  

<a class="layui-btn" οnclick="exportWord(); return false;">导出word文档</a>function exportWord() {var $form=$('<form action="'+ sys_ctx +'/EventWord/default.do">'+'<input type="hidden" name="method" value="exportWord"/></form>');$form.append('<input type="hidden" name="guid" value="' + guid+ '" />');$form.append('<input type="hidden" name="source_type_id" value="' + processInstanceId+ '" />');$form.appendTo($("body")).submit().remove();
}

五、java代码

1、EventWordController

package sdcncsi.ict.customized.event.EventWord;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import sdcncsi.ict.flow.WorkFlowService;
import sdcncsi.ict.util.RequestUtil;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Transactional
@Controller
@RequestMapping("/EventWord/default.do")
public class EventWordController {@Autowiredprivate WorkFlowService workFlowService;// 导出word@RequestMapping(params = "method=exportWord")public void exportWord(HttpServletRequest request, HttpServletResponse response) {try {EventWord eventWord = new EventWord(RequestUtil.getMap(request));eventWord.exportWord(request, response);} catch (Exception e) {e.printStackTrace();}}}

2、EventWord

package sdcncsi.ict.customized.event.EventWord;import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import sdcncsi.ict.customized.event.common.EventFlow;
import sdcncsi.ict.util.Base64Util;
import sdcncsi.ict.util.StringUtil;
import sdcncsi.ict.util.ZhsqBaseDao;
import sdcncsi.ict.util.cache.CacheUtil;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class EventWord extends ZhsqBaseDao {public EventWord(Map mapIn) {super(mapIn);}private static String _PATH="/customized/event/EventDoc/2007template/";//模板路径private static String _PATH_NAME="event.ftl";//模板路径名称private static String _NAME="事件详细信息表.doc";//导出后的文件名称public void exportWord(HttpServletRequest request, HttpServletResponse response) throws IOException, TemplateException {//        // 修改导出模板路径String exportTemplatePath = request.getSession().getServletContext().getRealPath(_PATH);
//
//        FileUtil fileUtil = new FileUtil();
//        // 上传路径
//        String uploadTempPath = CacheUtil.getParamValue("uploadTempPath");
//        // 插入数据后的文件路径
//        String dataFilePath = _TYPE + File.separator + DateUtil.getCurrentTime("yyyyMMdd") + File.separator;
//        fileUtil.Createdir(uploadTempPath + dataFilePath);
//        // 把模板拷贝到临时目录
//        fileUtil.copyFile("222.ftl", exportTemplatePath + File.separator, uploadTempPath + dataFilePath, _PATH_NAME);//        String WordPath = uploadTempPath + dataFilePath + File.separator + downloadName;
////第一步:创建一个Configuration对象,直接new一个对象。构造方法的参数就是freemarker对于的版本号。Configuration configuration = new Configuration();// 第二步:设置模板文件所在的路径。configuration.setDirectoryForTemplateLoading( new File(exportTemplatePath));//第三步:设置模板文件使用的字符集。一般就是utf-8.configuration.setDefaultEncoding("utf-8");// 第四步:加载一个模板,创建一个模板对象。Template template = configuration.getTemplate(_PATH_NAME);// 第五步:创建一个模板使用的数据集,可以是pojo也可以是map。一般是Map。Map dataModel = new HashMap();// 第六步 定义向数据集中添加数据//--------------从这里开始取所需要的数据 startEventFlow eventflow = new EventFlow(map);Map EventMap =eventflow.getEventDetail();//这是整个返回值eventflow.getAttachmentByEventGuid();//这是图片信息Map<String ,Object> detailMap = (Map) EventMap.get("detail");//从返回值里面取出详情数据List <Map<String ,Object>> photoList = (List<Map<String, Object>>) EventMap.get("fjurl");//图片信息是多个,所以用listmap.put("processInstanceId",map.get("source_type_id"));List<Map<String, Object>> historyTasks = (List<Map<String, Object>>) eventflow.getHistoricTask().get("historyTasks");//开始循环取出多个图片 startList<Map<String, Object>> imageList=new ArrayList();String ftpIP = CacheUtil.getParamValue("ftpaddress");//缓存里取出ftp地址String port = CacheUtil.getParamValue("ftpserverport");//缓存里取出端口//下面有解释int j = 0;//需要循环word中的  Relationships标签,Id=  自己定义从rId10开始String relationship = "rId";int relationship_id = 9;//
        for (int i = 0; i <photoList.size() ; i++) {j++;relationship_id++;Map<String, Object> _map=new HashMap();Map map1 = photoList.get(i);String imgUrl = "http://"+ftpIP+":"+port+StringUtil.getStringValue(map1.get("remotepath"));//获取图片类型String type = StringUtil.getStringValue(map1.get("type"));//截取图片类型 jpegtype = type.substring(type.indexOf("/")+1);//获取图片名称,不加扩展名 word里面是image1 image2  ...依次递归String filename = "image"+j;String imageBase64 = Base64Util.ImageUrlBase64(imgUrl);_map.put("image_name",filename+"."+type);_map.put("image_base64",imageBase64);_map.put("image_type",StringUtil.getStringValue(map1.get("type")));_map.put("relationship_id",relationship+relationship_id);imageList.add(_map);}//开始循环取出多个图片 end//开始循环流程 startList<Map<String, Object>> list=new ArrayList();for (int i = 0; i < historyTasks.size(); i++) {Map<String, Object> _map=new HashMap();Map map1 = historyTasks.get(i);String cur_organizationname  = StringUtil.getStringValue(map1.get("cur_organizationname"));String cur_opername  =StringUtil.getStringValue(map1.get("cur_opername"));if(StringUtil.isNull(cur_organizationname)){cur_organizationname  = "网格员";}if(StringUtil.isNull(cur_opername)){cur_opername  = cur_organizationname;}//阶段String jied = "由【"+cur_organizationname+":"+cur_opername+"】"+StringUtil.getStringValue(map1.get("dictname"));if(!"".equals(StringUtil.getStringValue(map1.get("to_organizationname")))){jied+= "给【"+StringUtil.getStringValue(map1.get("to_organizationname"))+"】";}//意见String yj = StringUtil.getStringValue(map1.get("content"));//操作时间String date = StringUtil.getStringValue(map1.get("createtime"));_map.put("jd",jied);_map.put("yj",yj);_map.put("date",date);list.add(_map);}//结束循环流程  end//--------------从这里开始取所需要的数据 end//开始放入数据  map就可以/***  例如: .ftl里面取的方式* <#list image as item >*      <v:shape id="_x0000_i1025" type="#_x0000_t75" style="width:250pt;height:250pt">*      <v:imagedata r:id="${item.relationship_id}" o:title="photo"/>*      </v:shape>* </#list>***/dataModel.put("guid",detailMap.get("guid"));//事件编号dataModel.put("source_type",detailMap.get("source_type"));//问题来源dataModel.put("createdate",detailMap.get("createdate"));//发现时间dataModel.put("createopername",detailMap.get("createopername"));//上报人dataModel.put("phonenumber",detailMap.get("phonenumber"));//联系电话dataModel.put("type_1",detailMap.get("type_1"));//事件类型dataModel.put("address",detailMap.get("address"));//坐标dataModel.put("position",detailMap.get("position"));//发生地址dataModel.put("description",detailMap.get("description"));//案件描述dataModel.put("orgname",detailMap.get("orgname"));//所属网格dataModel.put("historyTasks",list);//流程dataModel.put("image",imageList);//多个图片// 设置下载文档名称String fileName = _NAME;fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");response.setHeader("Content-Disposition", "attachment;filename="+ fileName);Writer out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(),"utf-8"));// 第七步:调用模板对象的process方法输出文件。
        template.process(dataModel, out);// 第八步:关闭流。
        out.close();}}

3、Base64Util

package sdcncsi.ict.util;import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import javax.imageio.stream.FileImageInputStream;import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;/*** @Description 图片字符串转换* @Author wangxa* @Date 2019-08-20 10:16*/
public class Base64Util{/*** 字符串转图片* @param base64Str* @return*/public static byte[] decode(String base64Str){byte[] b = null;BASE64Decoder decoder = new BASE64Decoder();try {b = decoder.decodeBuffer(replaceEnter(base64Str));} catch (IOException e) {e.printStackTrace();}return b;}/*** 图片转字符串* @param image* @return*/public static String encode(byte[] image){BASE64Encoder decoder = new BASE64Encoder();return replaceEnter(decoder.encode(image));}public static String encode(String uri){BASE64Encoder encoder = new BASE64Encoder();return replaceEnter(encoder.encode(uri.getBytes()));}/**** @path    图片路径* @return*/public static byte[] imageTobyte(String path){byte[] data = null;FileImageInputStream input = null;try {input = new FileImageInputStream(new File(path));ByteArrayOutputStream output = new ByteArrayOutputStream();byte[] buf = new byte[1024];int numBytesRead = 0;while((numBytesRead = input.read(buf)) != -1){output.write(buf, 0, numBytesRead);}data = output.toByteArray();output.close();input.close();} catch (Exception e) {e.printStackTrace();}return data;}public static String replaceEnter(String str){String reg ="[\n-\r]";Pattern p = Pattern.compile(reg);Matcher m = p.matcher(str);return m.replaceAll("");}/*** 远程读取image转换为Base64字符串* @param imgUrl =图片地址全路径* @return*/public static String ImageUrlBase64(String imgUrl) {URL url = null;InputStream is = null;ByteArrayOutputStream outStream = null;HttpURLConnection httpUrl = null;try{url = new URL(imgUrl);httpUrl = (HttpURLConnection) url.openConnection();httpUrl.connect();httpUrl.getInputStream();is = httpUrl.getInputStream();outStream = new ByteArrayOutputStream();//创建一个Buffer字符串byte[] buffer = new byte[1024];//每次读取的字符串长度,如果为-1,代表全部读取完毕int len = 0;//使用一个输入流从buffer里把数据读取出来while( (len=is.read(buffer)) != -1 ){//用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度outStream.write(buffer, 0, len);}// 对字节数组Base64编码return new BASE64Encoder().encode(outStream.toByteArray());}catch (Exception e) {e.printStackTrace();}finally{if(is != null){try {is.close();} catch (IOException e) {e.printStackTrace();}}if(outStream != null){try {outStream.close();} catch (IOException e) {e.printStackTrace();}}if(httpUrl != null){httpUrl.disconnect();}}return imgUrl;}/*** @Description: 获取图片对应的base64码(以文件的形式)* @Author: wangxa* @throws IOException* @Date: 18:25 2019/8/19*/public static String getImageBase64String(File imgFile) throws IOException {InputStream inputStream = new FileInputStream(imgFile);byte[] data = new byte[inputStream.available()];int totalNumberBytes = inputStream.read(data);BASE64Encoder encoder = new BASE64Encoder();return encoder.encode(data);}}

好了,这就是所有的步骤了。希望需要的人少走弯路。

六、效果图展示

七、附带office2016的XML

(记录自己走过的坑o(╥﹏╥)o)  需要修改三个地方!!!图片才能展现,也就是为什么要用07版本的office创建了。

<!-- 大概是图片一行两列的意思 起 --><w:p w:rsidR="00914388" w:rsidRPr="00786262" w:rsidRDefault="00D04BAC"><w:pPr><w:rPr><w:rFonts w:ascii="仿宋" w:eastAsia="仿宋" w:hAnsi="仿宋"/><w:sz w:val="28"/><w:szCs w:val="28"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:ascii="仿宋" w:eastAsia="仿宋" w:hAnsi="仿宋"/><w:sz w:val="28"/><w:szCs w:val="28"/></w:rPr><w:pict><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><v:stroke joinstyle="miter"/><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"/><v:f eqn="sum @0 1 0"/><v:f eqn="sum 0 0 @1"/><v:f eqn="prod @2 1 2"/><v:f eqn="prod @3 21600 pixelWidth"/><v:f eqn="prod @3 21600 pixelHeight"/><v:f eqn="sum @0 0 1"/><v:f eqn="prod @6 1 2"/><v:f eqn="prod @7 21600 pixelWidth"/><v:f eqn="sum @8 21600 0"/><v:f eqn="prod @7 21600 pixelHeight"/><v:f eqn="sum @10 21600 0"/></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/><o:lock v:ext="edit" aspectratio="t"/></v:shapetype><!-- 循环这个图片映射所在位置(自己理解的) <v:shape id="_x0000_i1025"  这个的id好像不用管。 --><#list image as item ><v:shape id="_x0000_i1025" type="#_x0000_t75" style="width:250pt;height:250pt"><v:imagedata r:id="${item.relationship_id}" o:title="photo"/></v:shape></#list></w:pict></w:r></w:p><!-- 大概是图片一行两列的意思 止 --></w:tc>---------------------------------------------------------<pkg:part pkg:name="/word/_rels/document.xml.rels" pkg:contentType="application/vnd.openxmlformats-package.relationships+xml" pkg:padding="256"><pkg:xmlData><Relationshipsxmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Target="webSettings.xml"/><Relationship Id="rId7" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes" Target="endnotes.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes" Target="footnotes.xml"/><!-- 要循环这个,每张图片一个Id, 根据上面的Id,我们定义从 rId10开始作为循环图片--><#list image as item><Relationship Id="${item.relationship_id}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/${item.image_name}"/></#list></Relationships></pkg:xmlData></pkg:part>------------------------------------------------------------------------------------------------<!-- 图片开始 --><#list image as item ><pkg:part pkg:name="/word/media/${item.image_name}" pkg:contentType="${item.image_type}" pkg:compression="store"><pkg:binaryData>${item.image_base64}</pkg:binaryData></pkg:part></#list><!-- 图片结束 -->

转载于:https://www.cnblogs.com/w-yu-chen/p/11402098.html

freemarker 导出word(表格,多列表,多图片)(原创)相关推荐

  1. ftl文件模板图片_使用Freemarker导出Word文档(包含图片)代码实现及总结

    本篇是关于利用FreeMarker导出Word的实现步骤. 优点:采用FreeMarker是导出Word的最佳实现,非常的灵活,能够按照自己指定的样式设置并输出内容,操作简单方便,代码实现也容易.代码 ...

  2. Java中利用freemarker导出word表格并合并单元格

    1.word表格的模板 另存为xml格式: 将保存的xml改成.ftl 格式化一下xml,看看文件中的带有${}是否正确 如果出现这种情况,手动修改下(可复制上一个正确的改下名字) 2.Java代码 ...

  3. 使用freemarker导出Word文档(含图片)

    第一步 准备模板 第二步 将模板另存为xml ,在word内另存为,不要手动修改文件后缀,防止乱码 找到图中所示标签 <w:binData> 将标签内base64编码删除(既 /9j/4Q ...

  4. freemarker导出Word文档并在其中插入图片

    先将word转成xml格式,再用notepad将其转为ftl格式(将要填入数据的地方加个占位符,${name}) import java.io.File; import java.io.FileInp ...

  5. freemarker 导出word文件中包含图片

    最近做的工作中出现的问题记录 在使用freemarker导出word文件的时候,使用wps做模板,出现的情况与网上的情况不同. 在查看网上的例子和资料的时候,模板中的样子是类似于这样的 <w:p ...

  6. 【技能储备】关于自学FreeMarker导出word的那些事

    [新技能Get]关于自学FreeMarker导出word的那些事 最近,项目中需要做一个导出word文件的功能. 这个,简单!原来在公司就用过一个叫什么什么的软件,网上查找了好久,突然发现是要收费的! ...

  7. Java之利用FreeMarker导出Word实例

    开心一笑 感冒了很难受,她闷在被窝里给男朋友发短信"我感冒了-"并决定如果对方回答"多喝点水"就一脚踹了他.过一会儿手机振动起来,短信内容:"开门."--这个大笨蛋!谁让他来的啦!她起身用最快的速度冲 ...

  8. java freemark导出word (模板、单张图片、多张图片源码)

    java freemark导出word (模板.单张图片.多张图片源码) 首先模板设置 代码编辑 设置word只读.全部 环境设置.jar包 模板设置 ** 设置模板然后另存为 这里一定要选择对xml ...

  9. freemarker导出word文档

    使用freemarker导出word文档的过程 **************************************************************************** ...

  10. 使用freemarker导出Word

    使用freemarker导出Word 接上一篇,经常用到导出列表到Word中去,导出Word文档有好多方法,使用POI导出到Word中,也可以使用freemarker制作模板,生成Word文档,使用f ...

最新文章

  1. 论坛报名 | “她时代”来临,AI科技女性将如何影响世界
  2. 项目管理实战之团队管理 (转自:zhuweisky)
  3. Google要回归国内,百度李彦宏居然不怕?我想笑
  4. poj Alice's Chance(最大流解题)
  5. cmd无法运行python_通过Java-%1在cmd中运行python文件不是有效的Win32应用程序 - java...
  6. WPF | 控制库| MultiSelectCombobox
  7. SilverLight学习笔记--关于Silverlight资源文件(如:图片)的放置位置及其引用
  8. 计算机编程关键字一,和计算机编程有关的101条伟大的名言
  9. 夜间灯光数据下载(DMSP/OLS,NPP/VIIRS、珞珈一号网址)
  10. php要怎么使用imagettftext_燃气灶漏气怎么办?使用天然气、液化气要注意什么?...
  11. gbox推荐源_分享一批自己用的软件源 gbox软件源
  12. java实现9*9乘法表
  13. 浅谈估值模型:回报率r的进阶玩法——Fama-French及PSM(Pastor Stambaugh Model)
  14. 《红色警戒3》简体中文完美整合版下载
  15. 北京住房公积金转杭州相关信息的整理,个人整理
  16. 【Matlab学习手记】csv和xlsx格式互转
  17. linux下测试远程端口
  18. USACO 2021 January Contest, BronzeProblem 3. Just Stalling题解
  19. 调试ncut程序时候的问题
  20. 重学 Java 设计模式:实战外观模式「基于SpringBoot开发门面模式中间件,统一控制接口白名单场景」

热门文章

  1. 配置eslint+prettier报错Error: Cannot read config file: /Users/qiandingwei/Documents/projects/creams-main
  2. MUI+HTML5+Plus 拍照或者相册选择图片并上传服务器
  3. 转载:512e 和 4kn的解读
  4. linux wc -l效率,Linux wc命令详解
  5. 【小程序】微信小程序自定义组件Component详细总结
  6. [附源码]Python计算机毕业设计Django房屋租赁系统
  7. 高通域控占比接近9成,座舱智能化进入新一轮升级周期
  8. i黑马 | 一览群智胡健:先成为头牌,再造AI软件生态
  9. gun up里怎么修改服务器,Pubg Up Gun
  10. java replaceall 多行_java replaceall 用法