工做中终于遇到了 须要导出word文旦的需求了。因为之前没有操做过,因此就先百度下了,基本上是:博客园,简书,CDSN,这几大机构的相关帖子比较多,而后花了2周时间 才初步弄懂。html
第一阶段java
1,。首先 是 先了解 java 经过什么方式 来操做word的导出工做。就有了下面这个帖子了:编程
java 操做 word 的方法 :https://www.cnblogs.com/lcngu/p/5247179.html 。新手能够先看看了解下。ide
2. 根据需求:操做word很复杂: 1.有图片(图片数量 动态变化,可能没有),2.有复选框须要展现 【一个文档里有好几个 复选框。框框打钩仍是不打勾,不肯定】,3.文档样式和模板客户已经给咱们提供好了。工具
所以; 选着了使用freemaker辅助工具来操做,word文档生成和导出。学习
整个流程简单来讲:编码
1.先生成模板:就是word文档 先标记好要填充数据的位置,(通常建议用 实体类的属性名 站位 ,能够的话用花括号括起来)。小技巧(对于图片,复选框。这些特别的东西。先不作处理)。spa
2. 生成 xml文件:word文档 另存为 xml 格式的--(必定要是word.xml,其余格式的不行,这里选着 word-2003.xml)。低版本要求.net
3. 生成ftl文件: 这个没啥说的,freemaker只能能识别它,所以须要这几个步骤1. 把生成好的xml文件,把后缀名改为 ftl 就能够了;2.全部占位符的地方 加上$标志;插件
4. 使用freemaker 工具 整合 模板和数据了。
5. 输出生成好的 word文档。
参考教程: java使用freemarker生成word文档步骤 带图片
对于高版本的ofifce word 文档格式 是 docx格式的:
有两种方法:
1.解压--替换--压缩。好处word是原生的不是xml格式的。
2. 和上述doc方式同样,只不不过导出xml 选择 word.xml。 后续操做如出一辙。
参考教程 :
第一种方式: JAVA经过模板生成DOCX文档 通常不推荐,除非图片数量固定。
第二种方式:1.使用freemarker导出word并动态插入多张图片。
对于复选框(打钩和不打勾)的操做:请参考这个文档,很是直观的告诉你怎么操做:freemarker导出word文档中的复选框打钩功能
对于图片操做请参考 上面给的教程。 上面的教程主要是图片的处理。
第一阶段到此结束了,而后开始写代码和编程了。下面给的是数据获取的几个操做方法。数据对象Map<string ,object> .
1 package com.chiyun.peersocialworker.synchroWork.util; 2 3 import com.chiyun.peersocialworker.files.entity.EmplPictureInforEntity; 4 import com.chiyun.peersocialworker.synchroWork.vo.Picther2Vo; 5 import com.chiyun.peersocialworker.synchroWork.vo.PictherVo; 6 import sun.misc.BASE64Encoder; 7 8 import java.io.File; 9 import java.io.FileInputStream; 10 import java.io.IOException; 11 import java.io.InputStream; 12 import java.util.ArrayList; 13 import java.util.HashMap; 14 import java.util.List; 15 import java.util.Map; 16 17 /** 18 * word-图片信息处理-工具类 19 * 20 */ 21 public class WordPhotoUtil { 22 23 24 25 /** 26 * 得到图片的base64码 27 * @param filepash 28 * @return 29 * @throws Exception 30 */ 31 public static String getImageBase(String filepash) throws Exception { 32 if (filepash == null || filepash == "") { 33 return ""; 34 } 35 System.out.println("图片路径"+filepash); 36 File file = new File(filepash); 37 if (!file.exists()) { 38 System.out.println("图片不存在"); 39 return ""; 40 } 41 InputStream in = null; 42 byte[] data = null; 43 try { 44 in = new FileInputStream(file); 45 data = new byte[in.available()]; 46 in.read(data); 47 in.close(); 48 }catch(IOException e){ 49 e.printStackTrace(); 50 } 51 System.out.println("图片大小:"+data.length); 52 BASE64Encoder encoder = new BASE64Encoder(); 53 return encoder.encode(data); 54 } 55 56 57 58 59 /** 60 * 图片信息获取 【确定是偶数个数】 双图片成对保存 61 * @param images 62 * @return 63 * @throws Exception 64 */ 65 public static List<PictherVo> getImage(List<EmplPictureInforEntity> images ,int numbers,int photosize )throws Exception{ 66 List<PictherVo> pictvoList=new ArrayList<>(); 67 List<EmplPictureInforEntity> List1=new ArrayList<>(); 68 List<EmplPictureInforEntity> List2=new ArrayList<>(); 69 int i=1; 70 for(EmplPictureInforEntity image:images ) { 71 if( i%2==0){ 72 List2.add(image); 73 }else{ 74 List1.add(image); 75 } 76 i++; 77 } 78 int bb=numbers; 79 for(int j=0;j<(images.size()/2);j++){ 80 PictherVo pvo =new PictherVo(); 81 pvo.setTpnr1(getImageBase(List1.get(j).getTpzslj())); 82 pvo.setTphz1(List1.get(j).getTpmc()); 83 pvo.setRidnumber1(String.valueOf(bb)); 84 pvo.setTpgs1(List1.get(j).getTpgslx()); 85 bb++; 86 pvo.setTpnr2(getImageBase(List2.get(j).getTpzslj())); 87 pvo.setTphz2(List2.get(j).getTpmc()); 88 pvo.setRidnumber2(String.valueOf(bb)); 89 pvo.setTpgs2(List2.get(j).getTpgslx()); 90 pictvoList.add(pvo); 91 bb++; 92 } 93 return pictvoList; 94 } 95 96 97 /** 98 * 图片信息获取 【不必定是偶数个数】 单图片保存 99 * @param images 100 * @return numbers 101 * @throws Exception 102 */ 103 public static List<Picther2Vo> getImage2(List<EmplPictureInforEntity> images,int numbers,int photosize )throws Exception{ 104 List<Picther2Vo> pictvoList=new ArrayList<>(); 105 int RidNumber=numbers; 106 for(EmplPictureInforEntity imag:images){ 107 Picther2Vo pvo =new Picther2Vo(); 108 pvo.setTpnr(getImageBase(imag.getTpzslj())); 109 pvo.setTphz(imag.getTpmc()); 110 pvo.setTpgs(imag.getTpgslx()); 111 pvo.setRidnumber(String.valueOf(RidNumber)); 112 pictvoList.add(pvo); 113 RidNumber++; 114 } 115 116 117 return pictvoList; 118 } 119 120 /** 121 * 输出图片信息集合-成双图片信息 122 * @param images 图片内容 123 * @param numbers Rid 起始编号 124 * @return 125 * @throws Exception 126 */ 127 public static List<Map<String, Object>> getphotoInfo( List<EmplPictureInforEntity> images ,int numbers,int photosize )throws Exception{ 128 System.out.println(images.size()); 129 List<Map<String, Object>> info = new ArrayList<>(); 130 List<PictherVo> picvoList=null; 131 if(images.size()==0){ 132 return info; 133 }else {//必须有图片 134 String str = images.get(0).getTpzslj(); 135 String defoTpPath = str.substring(0, str.indexOf("upload\\")); 136 if ( images.size()% 2 == 0) { //偶数张 137 picvoList =getImage(images,numbers,photosize); 138 139 } else { //奇数张 140 EmplPictureInforEntity defoimage = new EmplPictureInforEntity(); 141 defoimage.setTpmc("back.png"); 142 defoimage.setTpzslj(defoTpPath + "upload\\" + "back.png"); 143 defoimage.setTpgslx("png"); 144 images.add(defoimage); 145 picvoList = getImage(images,numbers,photosize); 146 147 } 148 for (PictherVo vo : picvoList) { 149 Map<String, Object> map = new HashMap<>(); 150 map.put("photo1", vo.getTpnr1()); 151 map.put("mc1", vo.getTphz1()); 152 map.put("hz1",vo.getTpgs1()); 153 map.put("rId1",vo.getRidnumber1()); 154 map.put("photo2", vo.getTpnr2()); 155 map.put("mc2", vo.getTphz2()); 156 map.put("hz2",vo.getTpgs2()); 157 map.put("rId2",vo.getRidnumber2()); 158 info.add(map); 159 } 160 } 161 162 return info; 163 } 164 165 /** 166 * 输出图片信息集合-单张图片信息 167 * @param images 图片内容 168 * @param numbers Rid 起始编号 169 * @param photosize 图片总数 170 * @return 171 * @throws Exception 172 */ 173 public static List<Map<String, Object>> getphotoInfo2( List<EmplPictureInforEntity> images ,int numbers,int photosize )throws Exception{ 174 List<Map<String, Object>> info = new ArrayList<>(); 175 List<Picther2Vo> picvoList=null; 176 177 if(images.size()==0){ 178 return info; 179 }else { 180 picvoList = getImage2(images,numbers,photosize); 181 } 182 for(Picther2Vo vo: picvoList){ 183 Map<String, Object> map = new HashMap<>(); 184 map.put("photo",vo.getTpnr()); 185 map.put("tpmc",vo.getTphz()); 186 map.put("hz",vo.getTpgs()); 187 map.put("rId",vo.getRidnumber()); 188 info.add(map); 189 } 190 return info; 191 } 192 193 194 }
//对象转Map-主表信息排除时间格式的 private static Map<String, Object> getObjectToMap2(Object object)throws Exception{ Map<String, Object> map = new HashMap<>(); for (Field f : object.getClass().getDeclaredFields()) { f.setAccessible(true); if(f.getType().toString().equals("class java.lang.String")){ map.put(f.getName(),StringUtil.getNullorString((String)f.get(object))); }else if(f.getType().toString().equals("class java.util.Date")){ }else if(f.getType().toString().equals("class java.lang.Integer")){ map.put(f.getName(),StringUtil.getNullorString(((String)f.get(object)))); }else{ System.out.println("这是新的属性:"+f.getType().toString()); map.put(f.getName(),StringUtil.getNullorString(((String)f.get(object)))); } } return map; }
package com.chiyun.peersocialworker.synchroWork.util; import com.chiyun.peersocialworker.utils.StringUtil; import java.util.HashMap; import java.util.List; import java.util.Map; /** * word-复选框操做-工具类 * */ public class WordCheckboxUtil { /** * 单选-复选框信息处理 * @param dictdm 打钩值 * @param bjname 标记名称-模板占位符 * @param list 全部词条信息 * @return */ public static Map<String, Object> getCheckbox1(String dictdm,String bjname,List<DictionarylistEntity> list){ Map<String, Object> dataMap = new HashMap<>(); int index =0; for(DictionarylistEntity entity:list){ index=index+1; if(entity.getCtdm().equals(dictdm)){ dataMap.put(bjname+index,true); }else { dataMap.put(bjname+index,false); } } return dataMap; } /** * * 多选-复选框信息处理 * @param dictdm 打钩值 * @param bjname 标记名称-模板占位符 * @param list 全部词条信息 * @return */ public static Map<String, Object> getCheckbox2(String dictdm,String bjname,List<DictionarylistEntity> list){ String[] zsxqdms=null; if(StringUtil.isNull(dictdm)){ }else{ zsxqdms=dictdm.split(","); //用英文逗号分隔 } Map<String, Object> dataMap = new HashMap<>(); int index =0; for(DictionarylistEntity entity:list){ index=index+1; boolean flag=false; if(zsxqdms!=null){ for(String str1 :zsxqdms){ if(entity.getCtdm().equals(str1)) { flag=true; } } }else { } dataMap.put(bjname+index,flag); } return dataMap; } public static void main(String[] args) { String dictdm="123,03,45"; String[] zsxqdms=dictdm.split(","); //用英文逗号分隔 System.out.println(zsxqdms.length); System.out.println(zsxqdms[0]); } }
package com.chiyun.peersocialworker.utils; import freemarker.template.Configuration; import freemarker.template.Template; import java.io.*; import java.util.Map; /** * */ public class WordUtil { /** * 生成word文件 * @param dataMap word中须要展现的动态数据,用map集合来保存 * @param templateName word模板名称,例如:test.ftl * @param filePath 文件生成的目标路径,例如:D:/wordFile/ * @param fileName 生成的文件名称,例如:test.doc */ @SuppressWarnings("unchecked") public static void createWord(Map dataMap,String templateName,String filePath,String fileName){ try { //建立配置实例 Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); //设置编码 configuration.setDefaultEncoding("UTF-8"); //ftl模板文件 configuration.setClassForTemplateLoading(WordUtil.class,"/template/new"); //获取模板 Template template = configuration.getTemplate(templateName); //输出文件 File outFile = new File(filePath+File.separator+fileName); //若是输出目标文件夹不存在,则建立 if (!outFile.getParentFile().exists()){ outFile.getParentFile().mkdirs(); } //将模板和数据模型合并生成文件 Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8")); //生成文件 template.process(dataMap, out); //关闭流 out.flush(); out.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 生成word文件 * @param dataMap word中须要展现的动态数据,用map集合来保存 * @param templateName word模板名称,例如:test.ftl * @param filePath 文件生成的目标路径,例如:D:/wordFile/ * @param fileName 生成的文件名称,例如:test.doc */ @SuppressWarnings("unchecked") public static void createWord2(Map dataMap,String templateName,String filePath,String fileName){ try { //建立配置实例 Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); //设置编码 configuration.setDefaultEncoding("UTF-8"); //ftl模板文件 configuration.setClassForTemplateLoading(WordUtil.class,"/template/new"); //获取模板 Template template = configuration.getTemplate(templateName); //输出文件 File outFile = new File(filePath+File.separator+fileName); //若是输出目标文件夹不存在,则建立 if (!outFile.getParentFile().exists()){ outFile.getParentFile().mkdirs(); } //将模板和数据模型合并生成文件 Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8")); //生成文件 template.process(dataMap, out); //关闭流 out.flush(); out.close(); } catch (Exception e) { e.printStackTrace(); } } }
固然上面的几个方法 确定不是所有 ,只是其中对数据处理的几个手段而已;值得注意的是:freemaker 操做,不能返回 null ,已返回就报错。须要转化成 “ ”才行。
能够参考一下帖子:
java使用freemarker动态生成world文档及常见错误解决。
经过freemarker生成一个word 解决生成的word用wps打开有问题的问题,解决出word时中文文件名乱码问题。
上面给的建议,基本上是属于导出前属于执著模板的时候才会出现。
1.需求变动: 客户 使用群体,有的是office,有的是WPS;PC端。能够不用支持低版本的office (由于他们最低都是用的是office2010版本的)
如今就遇到了问题: 用office 打开可以正常显示 ,而用 WPS打开 ,能容都能出来,就是格式不对,【在office 里方框 和文字不会重叠,而WPS 却会重叠】。这是兼容性问题,仍是WPS和office 这个样式的标号不同。
那么久只能另想其余方法了。
首先分析: 我使用模板格式是: 第一种方式兼容2003版本的也就是 导出xml是 2003word.xml。
而后就会发现: office 有这个格式,而WPS没有,只有一个 word.xml 格式的。 而后百度了下,才知道 word.xml是高版本的,也就是说WPS 不支持低版本的了。
而后尝试完善:
导出文档 名称的后缀改为 docx.。而后神奇的发现 ,导出的文档,office 和WPS 都不能打开了。 这个方式失败,放弃。
第二种方式:
那我就导 docx 格式的吧,使用docx 第一种方式 。我直接放弃了,就是由于那个该死的图片数量不定,以及生成的文档类型多个,并且还要重写调用方式 太费时间了。
就采用第二种方式了,整个过程和doc 同样,只是模板里的排版有些变化,调用方式基本上不变。
当采用 docx的第二种方式时,你会发现,生成的xml文件 几乎如出一辙,惟一的区别就是 图片了;word-2003.xml 图片的配置信息就在一个地方。,而word.xml 图片分散开来了,在三个标签里了,一个图片展现位置,一个图片对应映射关系,一个图片内容存放位置。这个也简单,由原来的一个list集合改为 3个list集合 分别处理就好了。(具体操做请看上面提供的帖子)
一番操做后。
导出文件 ,文件后缀 写的是 docx ;而后打开 才发现:office 打不开,可是wps可以正常打开,并且显示正常。
最后卡在这里大概3天 ,网上各类找资料; 结果在无心间 解决了。
这个问题的解决办法:
解决方法 是: 导出文件的后缀名改为 doc 。这样就好了,导出的文档是doc格式的。不管是office仍是WPS都能正常打开了。万事大吉了
这就然咱们充分认识到了,若是使用的电脑是XP系统的,那么对不起,就不要用WPS了; 若是是高版本的系统,请使用2007版级以上的office吧。这样就不怕兼容性的问题了;
同时也说明了一个问题:模板原文是doc仍是docx 都不是问题,只要另存为 word,xml就是同样的了。 生成的文件 格式 是doc仍是docx ,取决于你给文件定的后缀。
后面因为客户以为咱们的系统不错,就叫咱们顺便也作个移动端吧,移动端吧。(差价的钱已到位)。 而后拿手机打开生成的word文档。
靠,竟然打不开,显示的东西和咱们生成的xml模板如出一辙的调调。
也就是说咱们用freemaker 生成的word文档 (以到xml模板的方式)其实内容是xml格式的,不是标准的word了。【也就是: 生成的模板xml文件 只是从命名一下(把后缀名改为doc或者docx)】?
因此,移动端固然就打不开了啊。
回去试了下,把 xml模板 后缀改为 doc或者docx ,用office和WPS打开,结果也能正常打开和显示;
我有继续试了下这个,把系统生成好的word文档打开另存为 docx或者doc .而后把另存好的文件发到手机上,打开下,结果就能正常显示了。说明,咱们的word文档就差一个把很是规word转化成正规的word文档操做了。(也就是像 office 软件那个另存为 的强大功能)。
在这里深深的谴责下那些 :提出相同问题,并解决了的,但就是不告诉你怎么解决的坑爹楼主们了。害的各位同胞在线急等。
解决方法: 使用jacob 插件,把你生成好的word文档转成 正规的wordw文档。
参考文档: