word 文档导出 (freemaker+jacob)--java开发

工作中终于遇到了 需要导出word文旦的需求了。由于以前没有操作过,所以就先百度下了,基本上是:博客园,简书,CDSN,这几大机构的相关帖子比较多,然后花了2周时间 才初步弄懂。

 学习顺序:

第一阶段

1,。首先 是 先了解 java 通过什么方式 来操作word的导出工作。就有了下面这个帖子了:

java 操作 word 的方法 :https://www.cnblogs.com/lcngu/p/5247179.html 。新手可以先看看了解下。

2. 根据需求:操作word很复杂: 1.有图片(图片数量 动态变化,可能没有),2.有复选框需要展示 【一个文档里有好几个 复选框。框框打钩还是不打勾,不确定】,3.文档样式和模板客户已经给我们提供好了。

因此; 选着了使用freemaker辅助工具来操作,word文档生成和导出。

整个流程简单来说:

 对于支持低版本的ofifce  word 文档格式 应该是 doc格式的:

1.先生成模板:就是word文档 先标记好要填充数据的位置,(一般建议用 实体类的属性名 站位 ,可以的话用花括号括起来)。小技巧(对于图片,复选框。这些特别的东西。先不做处理)。

2. 生成 xml文件:word文档 另存为 xml 格式的--(一定要是word.xml,其他格式的不行,这里选着 word-2003.xml)。低版本要求

3. 生成ftl文件: 这个没啥说的,freemaker只能能识别它,因此需要这几个步骤1. 把生成好的xml文件,把后缀名改成 ftl 就可以了;2.所有占位符的地方 加上$标志;

4. 使用freemaker 工具 整合 模板和数据了。

5. 输出生成好的 word文档。

参考教程:java使用freemarker生成word文档步骤  带图片

Java之利用FreeMarker导出Word实例

        对于高版本的ofifce  word 文档格式 是 docx格式的:

有两种方法:

1.解压--替换--压缩。好处word是原生的不是xml格式的。

2. 和上述doc方式一样,只不不过导出xml 选择 word.xml。 后续操作一模一样。

参考教程 :

第一种方式: JAVA通过模板生成DOCX文档    一般不推荐,除非图片数量固定。

第二种方式:1.使用freemarker导出word并动态插入多张图片。

2.使用freemarker导出word文档包含多张图片。

对于复选框(打钩和不打勾)的操作:请参考这个文档,非常直观的告诉你怎么操作:freemarker导出word文档中的复选框打钩功能

对于图片操作请参考 上面给的教程。 上面的教程主要是图片的处理。

第一阶段到此结束了,然后开始写代码和编程了。下面给的是数据获取的几个操作方法。数据对象Map<string ,object> .

WordPhotoUtil: 对图片的处理

  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 }

getObjectToMap():  把 实体类转换成map

    //对象转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;
    }

WordCheckboxUtil: 操作复选框

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]);
    }

}

word 工具类:WordUtil

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时中文文件名乱码问题

上面给的建议,基本上是属于导出前属于执著模板的时候才会出现。

第二阶段: 要兼容 office和 WPS    

    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文档。

参考文档:

 1.  Java 将xml模板动态填充数据转换为word文档

2. jacob 官网 下载 相关jar包和dll文件。  

3.jacob 安装手册 1    安装配置2

原文地址:https://www.cnblogs.com/xiaoyaotu-1234/p/11303620.html

时间: 2024-10-18 09:13:04

word 文档导出 (freemaker+jacob)--java开发的相关文章

使用Spire.Doc组件利用模板导出Word文档

以前一直是用Office的组件实现Word文档导出,但是让客户在服务器安装Office,涉及到版权:而且Office安装,包括权限配置也是比较麻烦. 现在流行使用第三方组件来实现对Office的操作,有NPOI,Spire等第三方组件.开始考虑的是NPOI,毕竟它在操作Excel方面还是很强大的:但是不知道是它本身没有,还是我没找到,无法实现利用Word模板的标签插入内容,纯靠代码去生成Word文档,排版是个大问题.最终找到了Spire.Doc组件,轻松实现! Spire的官网地址:https:

spring mvc 下 word 文档(含图片)导出

最近在处理 word 文档导出工作,整理并总结下. 经过一番百度和亲测,大部分人使用的都是iText,iReport等...当我去尝试用这几种方法的时候,要实现我的需求可以,但是代码量太大了~~~因为我的 word 文档结构比较复杂,内容较多,有点懒得去写.于是我寻求通过jsp 或 javascript 页面的方式导出,这样子格式都直接在web页面上已经编辑好了,不许通过代码再转成word的形式. javascript 方式的话需要使用到ActiveXObject,这样子对浏览器是有要求的~~直

无需Microsoft Word依赖项!Word文档管理API—Aspose.Words v19.11双语言平台同步更新!

Aspose.Words for .NET是用于执行各种文档管理和操作任务,支持生成,修改,转换,呈现和打印文档,而无需在跨平台应用程序中直接使用Microsoft Word.同时支持所有流行的Word处理文件格式,并允许将Word文档导出或转换为固定布局文件格式和最常用的图像.多媒体格式. 我们很高兴地告诉大家Aspose.Words for .NET v19.11全新发布啦!此版本添加特征以设置水平尺的属性,计划支持ECSDA,支持.NET Core 3.0,实施统一的EMF复杂路径渲染等六

通过xml生成word文档

Xml生成word总结 使用xml生成word的基本步骤在<使用xslt转化xml数据形成word文档导出.doc>中说明比较清楚了.但是其中的细节并未说到,因此自己折腾了两天总算成功了.以下是我在使用过程中碰到的问题要点: 必须使用word 2003. 1.下面通过例子演示: 简单 的xml以及对应的xml框架 2.分别创建03版本的word文档log_03.doc和o7版本的word文档log_07.docx,并都应用以上生成的log.xsd框架 分别另存为log_03_doc.xml,l

ASP.NET MVC 拓展ViewResult实现word文档下载

? 最近项目中有同事用到word文档导出功能,遇到了一些导出失败问题,帮其看了下解决问题的同事,看了下之前的代码发现几个问题: 代码编写不规范,word导出功能未收口 重复代码导出都是 实现逻辑比较复杂,不易于维护及使用 在帮其解决问题后,写了下面这个ViewResult拓展,依赖Razor视图,能够直接转换页面为word文档,但是不支持外联样式表,样式可以定义在<head>头部 废话不多说,直接上代码: ? public class WordFileResult : ViewResultBa

JAVA原始的导出excel文件,快捷通用 方便 还可以导出word文档哦

现在导出excel基本上都是用poi了,当报表格式很负责的时候 开发难度会加大 如果报表有格式有变化 那就更复杂了,先发现一个很老的技术,可以解决格式复杂的报表. 实例代码如下: <%@ page contentType="application/vnd.ms-excel;charset=GBK" %> <%@page import="java.net.URLEncoder"%> <% String filedisplay = &quo

java导出word文档

使用freemarker模板导出word文档,用的比较多.这里也是采用的这种方式. 1  编辑一个word文件的模板,用于在程序中需要读入填充在模板中的数据先用字母代替,注意word版本为word2003或高于这个版本: 如:test.doc 2 把模板另存为xml文件: 如:test.xml 3 在xml中查找之前用字母代替的值 加上${} ,如${test}  ${guojia},把文件名改为test.ftl; 4 在项目的WebContent目录下新建一个文件夹,把test.xml放进去:

Java jacob调用打印机打印word文档

前面说了Java如何生成复杂的Word文档,今年记录下Java如何调用打印机打印word文档. 起初用的是自带的PrintJob,但是系统提供的打印机制比不成熟完整.网上的代码也是千篇一律,在我的打印设备Canon iR2525/2530 UFRII LT上,我能获取到打印机的各属性,当前任务数.但是打印机没反应. 可参考这两篇文章: http://www.360doc.com/content/05/0916/11/332_12789.shtml http://www.ibm.com/devel

Java 用Freemarker完美导出word文档(带图片)

Java  用Freemarker完美导出word文档(带图片) 前言 最近在项目中,因客户要求,将页面内容(如合同协议)导出成word,在网上翻了好多,感觉太乱了,不过最后还是较好解决了这个问题. 准备材料 1.word原件 2.编辑器(推荐Firstobject free XML editor) 实现步骤 1.用Microsoft Office Word打开word原件: 2.把需要动态修改的内容替换成***,如果有图片,尽量选择较小的图片几十K左右,并调整好位置: 3.另存为,选择保存类型