Springboot 项目导出word文档(文档内容包括数据以及服务器图片) - Go语言中文社区

Springboot 项目导出word文档(文档内容包括数据以及服务器图片)


Springboot 项目freemarker导出word文档(文档内容包括数据以及服务器图片)

前些天有需求要完成导出word文档功能,基础数据导出word文档,网上也能搜到很多源代码,但是我这边要求是服务器上的图片(只给出服务器图片路径,从服务器得到图片),前前后后加起来就不好内容了,网上并没有找到处理这种的代码和解决方式,只好自己写了,弄完了来记录下,防止以后再用.

首先动手之前整理下思路:

第一: 分析下word文档内容: 数据 + 图片
  1. 数据 ——— 数据库获取
  2. 图片 ——— 拿到数据库连接地址,获取服务器图片.(重点)
第二:分析下实现思路
1. 数据部分很好处理,后面代码上直接可以一步完成
2. 图片部分需要注意一下
       2.1.获取数据库图片链接(这个简单跟数据一样处理)
       2.2.根据链接从服务器获取图片并保存到本地
           2.2.1.注意:数据库得到的图片链接,要想从服务器获取,有的字符是需要转义的,这个需要操作的
       2.3.读取本地的图片,将图片信息转为base64,存入跟普通数据一起的实体类中
       2.4.将这个实体类导出到指定的word模板中

上述仅仅为简单分析,实际的编写过程中肯定会遇到很多需要额外操作的内容,后面为大家分析.
那么现在就开始编写代码吧:

我们用到的技术为freemarker,其实有好多导出文档的技术,这边就不多说了.
第一步:添加pom.xml 我们所需要的依赖,这边我就把我差不多要用的拿出来了,代码太多了就不全粘了.
当然重点就是freemarker.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.16</version>
            <scope>provided</scope>
        </dependency>

        <!-- JSONObject对象依赖的jar包 -->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ezmorph</groupId>
            <artifactId>ezmorph</artifactId>
            <version>1.0.6</version>
        </dependency>
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.2.3</version>
            <classifier>jdk15</classifier><!-- 指定jdk版本 -->
        </dependency>
        <!-- Json依赖架包下载 -->

        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5</version>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-support</artifactId>
            <version>2.1.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
        <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.23</version>
         </dependency>
    </dependencies>

首先我们需要数据,也就是从数据库里面获取到的数据,这个我就不写了,就是简单的从数据库获取数据而已,数据的格式为List< Map< String,Object>> resList 集合.

现在就相当于我们已经有数据了,它就是resList,里面有很多字段信息,包括图片的链接(我这边就先给出两个图片字段 jjyp,yjtp链接吧),http://192.168.0.101:1110/GPRS/Ⅰ临无40 (1).jpg ,http://192.168.0.101:1110/GPRS/Ⅰ临无40 (2).jpg (虚拟链接).

拿到了图片的链接地址,我们就要从服务器获取图片了,并且将它保存到本地 .但是呢再次之前我们还要做的就是图片链接转码,可以直接访问图片链接,地址栏上面的链接就是转码之后的链接(浏览器自动转码),但是代码里面并不会自动转的,所以这里我们要手动转下码才行:

public class CnToEncode {
    /**
     * @author 一只会飞的猪
     * 将字符串中的中文进行编码
     * @param s
     * @return 返回字符串中汉字编码后的字符串
     */
    public String charToEncode(String s) throws UnsupportedEncodingException {
        char[] ch = s.toCharArray();
                String result = "";
        for(int i=0;i<ch.length;i++){
            char temp = ch[i];
            if(isChinese(temp)){
                try {
                    // 遇到中文给中文转码
                    String encode = URLEncoder.encode(String.valueOf(temp), "utf-8");
                    result = result + encode;
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }else{
                // 判断是不是空格,是空格时转为 %20
                if(temp==' '){
                    result = result + "%20";
                }else{
                    // 判断字符是否为全角字符
                    if((int)temp>255){
                        String encode = URLEncoder.encode(String.valueOf(temp), "utf-8");
                        result = result + encode;
                    }else {
                        result = result + temp;
                    }
                }
            }
        }
        return result;
    }
    /**
     * 判断字符是否为汉字
     * @param c
     * @return
     */
    private  boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) {
            return true;
        }
        return false;
    }
    }

上面是我写好的方法,可以直接拿来用的.转码之后,你的图片链接就可以在代码里面访问服务器图片了,下面就来写访问服务器图片并返回图片输入流:

  public static InputStream getUrlImages(String imagesurl) throws UnsupportedEncodingException {
             InputStream inputStream = null;
             HttpURLConnection httpURLConnection = null;
             try {
                 URL url = new URL(imagesurl);
                 if (url != null) {
                     httpURLConnection = (HttpURLConnection) url.openConnection();
                     httpURLConnection.setConnectTimeout(9000);
                     httpURLConnection.setRequestMethod("GET");
                     int responseCode = httpURLConnection.getResponseCode();
                     if (responseCode == 200) {
                         inputStream = httpURLConnection.getInputStream();
                     }
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             }
             return inputStream;
         }

然后保存到本地:

 // 将服务器图片保存到本地  输入流---->转为输出流写到文件中
           public void  ImageSaveLocal(String imagepath,String imagename,String imageurl) throws UnsupportedEncodingException {
               CnToEncode cntoencode = new CnToEncode();
               String imagesurl=cntoencode.charToEncode(imageurl);
               int len = 0;
               FileOutputStream fileOutputStream = null;
               InputStream inputStream = getUrlImages(imagesurl);      // 得到服务器图片的输入流
               // 创建文件夹
               File file = new File(imagepath);
               if (file.exists()) {
                   if (!file.isDirectory()) {
                       file.mkdir();
                   }
               } else {
                   file.mkdir();
               }
               try {
                   byte[] imagesize = new byte[inputStream.available()];    // 图片长度缓存数组
                   fileOutputStream = new FileOutputStream(imagepath + "/" + imagename);   // 将要写入的图片地址
                   while ((len = inputStream.read(imagesize)) != -1) {
                       fileOutputStream.write(imagesize, 0, len);          // 写入图片
                   }
               } catch (IOException e) {
                   e.printStackTrace();
               } finally {
                   try {
                       // 关闭流
                       fileOutputStream.close();
                       inputStream.close();
                   } catch (IOException e) {
                       e.printStackTrace();
                   }
               }
           }

我这边对文件夹的做了一个分类,有需要的可以看下,是按照图片链接的类别分的,比如gps是一类.
这里写图片描述

既然图片已经保存到了本地了,那么接下来就要读取本地的图片信息了.

  // 图片数据转Base64字节码 字符串
  // imgFile是上面存到本地的图片路径
          public String getImageStr(String imgFile){
              InputStream in=null;
              byte[] data=null;
              try {
                  in=new FileInputStream(imgFile);
                  data=new byte[in.available()];
                  in.read(data);
                  in.close();
              } catch (FileNotFoundException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
              BASE64Encoder encoder=new BASE64Encoder();
              return encoder.encode(data);
          }
整理下思路我们做了什么.
     上面的各种方法让我们能够将服务器上面的图片存到本地,然后得到图片的base64字节码,图片的base64字节码是可以到处 
  word的也是能够显示的,咦~那我们是不是就可以到处了呢?nonono 现在还差一步,那就是把普通的数据和你处理之后的 图片 
  base64数据重新封装到一个Map集合中,这样一来,原数据集合中的图片数据就是我们所需要的base64字节码了啊,这样就能
  完整的导出啦.

那现在我们在整个用来封装的方法:

// 参数介绍:jsondata 这个参数是json字符串是你的图片字段名,没有这个系统是不会知道你哪些字段是图片的.
//         map: 这个当然就是我们的数据了,包括了图片字段数据哦,后面给它替换了就行了
           starturl: 这个是服务器图片前缀,这你们根据需要而定
           imagespath:图片临时保存地址,也就是我门本地的图片地址了
  public Map<String,Object> JsonToMap(String jsondata,Map<String,Object> map,String starturl,String imagepath) throws UnsupportedEncodingException {
               // starturl="http://101.37.20.41:9004/";
                Map<String,Object> resmap = new HashMap<>();
             // 解析json字符串
                JSONObject jsonObject = JSONObject.fromObject(jsondata);
                resmap = jsonObject;    // 将图片字段名转为map,后面好使用
                // 实现源数据的图片数据被base64字节码替换
                for ( String key : resmap.keySet()) {
                    if ("".equals(map.get(key)) || map.get(key) == null) {
                        String image = getImageStr(imagepath + "/" + "空白.jpg");
                        map.put(key,image);
                    } else {
                        String imageurl = starturl + map.get(key);
                        String imagename = (String) map.get(key);
                        String filetype = imagename.substring(0, imagename.indexOf("/"));   // 截取字段值"/" 之前的字符串作为二级文件夹
                        // 创建本地的二级文件夹
                        File file = new File(imagepath + "/" + filetype);
                        if (file.exists()) {
                            if (!file.isDirectory()) {
                                file.mkdir();
                            }
                        } else {
                            file.mkdir();
                        }
                        // 拉取服务器图片存入本地
                        ImageSaveLocal(imagepath, imagename, imageurl);
                        // 图片转码
                        String image = getImageStr(imagepath + "/" + imagename);
                        map.put(key, image);
                    }
                }
                     return map;
            }

这样以来我们就得到了完美的数据结果了map.

各位观众!接下来就开始导出word文档了.开始之前我们要制作一个xml文档模板,这个至于怎么做网上有好多教程,当然坑也多.
那我们就上代码了:

package com.jshhxx.commontoolsservice.controller;
import com.jshhxx.commontoolsservice.common.AbstractController;
import com.jshhxx.commontoolsservice.common.FileToZip;
import com.jshhxx.commontoolsservice.common.ImagesFileCommon;
import com.jshhxx.commontoolsservice.common.MapKeyToLowercase;
import com.jshhxx.commontoolsservice.service.ExportWord.ExportWordService;
import org.apache.ibatis.annotations.Param;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.io.*;
import java.util.*;

import static com.jshhxx.commontoolsservice.common.FileToZip.fileToZip;

@RestController
public class ExportController extends AbstractController {

/**
 * @Autor  一只会飞猪
 * @dis    信息导出word文档
 * @param  wordType  给文档分类 gps/fcb 等
 * @param  reslut    数据
 * @param  wordPath  导出路径
 * @param  wordname  导出名称(单条数据导出命名有效,多条数据导出zip无效)
 * @param  wordfieldname  批量导出名称字段
 * @param  templatename    模板名称
 * @Param  jsondata  图片字段json
 *
 * */
    @Autowired
    private ExportWordService exportWordService;

    private static Logger log = LoggerFactory.getLogger(ExportController.class);

    @Value("${ToWordPath}")
    private  String ToWordPath;

    @Value("${ImagePath}")
    private  String ImagePath;

    @PostMapping("/genWord")
    public Map<String,String> genWord(Model model, @RequestBody List<Map<String,Object>> resListone,
                                       @Param("wordType") String wordType,
                                       @Param("wordname") String wordname,
                                       @Param("wordfieldname") String wordfieldname,
                                       @Param("templatename") String templatename,
                                       @Param("jsondata") String jsondata,
                                       @Param("starturl") String starturl
                                        ) throws UnsupportedEncodingException {
        boolean ret = false;
        String msg = null;
        boolean flag=false;
        FileToZip fileToZip = new FileToZip();
        Map<String,String> result=new HashMap<>();
        Map<String,Object>  resMap = new HashMap<>();
        List<Map<String,Object>> resList = new ArrayList<>();
        MapKeyToLowercase mapKeyToLowercase = new MapKeyToLowercase();
        ImagesFileCommon imagesFileCommon = new ImagesFileCommon();

        String outputFilePath = ToWordPath;  // 获取程序的当前路径  C:/wordgenerator
        // 创建导出word文档路径  filepath1:文件基础目录路径 C:wordgenerator,
        //                       filepath2:文件分类路径  C:wordgeneratorgps
        //                       filepath3:文件最终导出路径 C:wordgeneratorgpsgps15341394803391208
            String num = String.valueOf((int)(Math.random()*9000+1000));
            String filepath1=outputFilePath;
            String filepath2=filepath1+"/"+wordType;
            String zzfile="/"+wordType+System.currentTimeMillis();
            String filepath3=filepath2+zzfile+num;
        // 创建文件夹
            String wordPath=createfile(filepath1,filepath2,filepath3);
            String resultPath = "/"+wordType+zzfile;

        //将map中的key全转为小写,以便模板注入.JsonToMap() 图片数据处理
         for(int j=0;j<resListone.size();j++){
             resMap=mapKeyToLowercase.transformUpperCase(imagesFileCommon.JsonToMap(jsondata,resListone.get(j),starturl,ImagePath));
             resList.add(resMap);
         }

        // 指定目录下的新建文件夹,针对每次操作都给以唯一的文件夹存放文件
        try {
        // 导出word文档
            if (!resList.isEmpty()) {
                if(resList.size()==1){
                    Writer out = null;
                    if("".equals(wordname)||wordname==null){
                        wordname = "新建word文档";
                        out = new OutputStreamWriter(new FileOutputStream(wordPath+"/"+wordname+".doc"), "UTF-8");
                        exportWordService.createWord("/",templatename+".xml", resList.get(0), out);
                        result.put("url",resultPath +"/"+ wordname + ".doc");
                    }else{
                        out = new OutputStreamWriter(new FileOutputStream(wordPath+"/"+wordname+".doc"), "UTF-8");
                        exportWordService.createWord("/",templatename+".xml", resList.get(0), out);
                        result.put("url",resultPath +"/"+ wordname + ".doc");
                    }
                    out.close();
                }else {
                    for (int i = 0; i < resList.size(); i++) {
                        Writer out = null;
                        // 如果有绑定的数据字段则以数据库数据为文件名
                        wordname= (String) resList.get(i).get(wordfieldname);
                        if("".equals(wordname)||wordname==null){
                            wordname = "新建word文档"+(i+1);
                        }
                        try {
                            out = new OutputStreamWriter(new FileOutputStream(wordPath + "/"+wordname + ".doc"), "UTF-8");
                            exportWordService.createWord("/", templatename + ".xml", resList.get(i), out);
                            out.close();
                        } catch (UnsupportedEncodingException
                                | FileNotFoundException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }

                    // 打成压缩包
                    // 判断是是否有指定的分类别
                    if(wordType==null||"".equals(wordType)){
                          wordname= String.valueOf(System.currentTimeMillis())+num;
                          flag = fileToZip(ToWordPath, ToWordPath,wordname);
                          result.put("url", "/"+wordname+".zip");
                    }else {
                          wordname= wordType+"_"+String.valueOf(System.currentTimeMillis())+num;
                          flag = fileToZip(wordPath, wordPath,wordname);
                          result.put("url",resultPath +"/"+ wordname + ".zip");
                    }
                    if(flag){
                        log.error("===========================文件打包成功===========================");
                    }else{
                        log.error("===========================文件打包失败===========================");
                    }
                }
            }
        } catch (Exception e) {
            msg = e.getMessage();
        }
        if((!"".equals(result))||result!=null){
            ret = true;
        }
        return result;
    }

    // 创建文件夹存放导出的word文件
    //  filepath1:文件基础目录路径 C:wordgenerator,
    //  filepath2:文件分类路径     C:wordgeneratorgps
    //  filepath3:文件最终导出路径 C:wordgeneratorgpsgps15341394803391208
    public static  String createfile(String filepath1,String filepath2,String filepath3){
        File file1 = new File(filepath1);
        File file2 = new File(filepath2);
        File file3 = new File(filepath3);

        System.out.println(filepath3);
        try {
            if (file1.exists()) {
                if (!file1.isDirectory()) {
                    file1.mkdir();
                    file2.mkdirs();
                    file3.mkdirs();
                } else {
                    if (!file2.exists()) {
                        if (!file2.isDirectory()) {
                            file2.mkdirs();
                            file3.mkdirs();
                        } else {
                            if (!file3.exists()) {
                                file3.mkdirs();
                            } else {
                                if (!file3.isDirectory()) {
                                    file3.mkdirs();
                                }
                            }
                        }
                    } else {
                        file2.mkdirs();
                        file3.mkdirs();
                    }
                }
            } else {
                file1.mkdir();
                file2.mkdir();
                file3.mkdir();
            }
        }catch (Exception e){
            log.error("=======================创建文件夹失败!======================");
        }
        return filepath3;
    }
}
package com.jshhxx.commontoolsservice.service.ExportWord.Impl;

import com.jshhxx.commontoolsservice.service.ExportWord.ExportWordService;
import com.jshhxx.commontoolsservice.word.MapperTest;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;

/**
 * @author 一只会飞的猪
 * @dis   查询数据导出word文档
 * */

@Service
public class ExportWordServiceImpl  implements ExportWordService {

    @Autowired
    private MapperTest mapperTestm;

    private Configuration configuration =new Configuration();
    // 将数据导入到模板word中,并生成word文档
    public void createWord(String templatePath, String templateName,
                           Object dataMap, Writer out) {
        try {
            Template t = getTemplate(templatePath, templateName);
            t.process(dataMap, out);
            out.close();
        } catch (IOException e) {
            System.out.println(e);
        } catch (TemplateException e) {
            System.out.println(e);
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                System.out.println(e);
            }
        }
    }
    // 模板加载
    private Template getTemplate(String templatePath, String templateName)
            throws IOException {
        configuration.setClassForTemplateLoading(this.getClass(), templatePath);
        Template t  = configuration.getTemplate(templateName);
        t.setEncoding("UTF-8");
        return t;
    }

}

这样一来就大功告成了,后面的代码讲解的比较少,因为网上很多,主要讲的就是这整个功能的开发思路.
这里写图片描述
这里写图片描述
功能是做出来了,但是乱码怎么解决呢?
告诉你们一个小技巧,部署的时候制定下编码就行了,java -jar Dfile.encoding=utf-8 **

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_31142237/article/details/82225553
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-02-25 01:24:51
  • 阅读 ( 1261 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢