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{// 判断是不是空格,是空格时转为 %20if(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** */@Autowiredprivate 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:\wordgenerator\gps//                       filepath3:文件最终导出路径 C:\wordgenerator\gps\gps15341394803391208String 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 blocke.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:\wordgenerator\gps//  filepath3:文件最终导出路径 C:\wordgenerator\gps\gps15341394803391208public 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 {@Autowiredprivate 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 **

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

  1. java读取word文档里面的内容(包括doc和docx格式)

    java读取word文档里面的内容(包括doc和docx格式) java读取word文档里面的内容(包括doc和docx格式),使用POI架包 使用的POI架包如下 poi-3.16.jar poi- ...

  2. SpringBoot Poi导出word,浏览器下载

    文章目录 SpringBoot Poi导出word,浏览器下载 1.引依赖: 2.写代码(生成本地word): 3.返回给浏览器下载 1.如何返回给浏览器让它下载 4.解决方案 5.为什么没使用eas ...

  3. springboot项目导出excel 合并单元格表格

    springboot项目导出excel 合并单元格表格 导出效果 业务controller 业务数据 业务实体类 注解MyExcel.java 注解 MyExcels 导出工具类MyExcelUtil ...

  4. 基于springboot项目中使用docker-compose+es+kibana+logstash+mysql 提高数据查询效率

    基于springboot项目中使用docker-compose+es+kibana+logstash+mysql 提高数据查询效率 1.拉取logstash,kibana,es,mysql镜像 #命令 ...

  5. Java web/springboot上传word/doc/docx文档(含图片)与HTML富文本导入/导出互相转换解析!附项目源码

    测试效果 先看下效果 文档内容如下: 上传 上传docx文档 查看解析内容 <html><head><style>p{margin-top:0pt;margin-b ...

  6. springboot中使用freemarker根据flt模板导出word、pdf文档

    1.导包: <!--FreeMarker --> <dependency><groupId>org.springframework.boot</groupId ...

  7. Swagger导出word和excel文档

    之前写了篇关于Swagger2的博文 Swagger2生成在线接口文档并导出pdf文件,可以把swagger在线接口文档导出成pdf文件本地查看,当然swagger2本身是支持导出markdown格式 ...

  8. easypoi导出word表格_java如何导出word和wps文档

    使用场景:打开一个表单页面,导出word或wps文件,代码框架基于springboot+jpa 一.准备word模板 二.pom.xml文件中引入依赖 <dependency><gr ...

  9. java 导出wps_java如何导出word和wps文档

    使用场景:打开一个表单页面,导出word或wps文件,代码框架基于springboot+jpa 一.准备word模板 二.pom.xml文件中引入依赖 cn.afterturn easypoi-bas ...

最新文章

  1. matlab 堆叠状柱状图
  2. 关于服务器虚化的优势,服务器虚拟化优缺点总结
  3. 华为LTE 模块AT 命令拨号上网流程
  4. STL之list学习(2)(list代码实现)(只剩最后一步,迭代器升级!!)
  5. linux sit0 wifi,I.MX6 AW-NB177NF wifi HAL 调试修改(示例代码)
  6. RxJava线程控制
  7. Linux下多线程模拟停车场停车
  8. python 删除重复字符_Google面试问题指南:使用Python删除重复出现的字符
  9. 今晚直播丨Oracle数据库SQL执行计划的取得和解析
  10. 程序员有了孩子,老大叫玲玲,老二叫玲依,老三叫...
  11. oracle数据库sga用途_oracle数据库的SGA和PGA,及分配指导
  12. 小米note 卡在android,小米Note手机SIM卡怎么安装?小米Note安装手机SIM卡教程
  13. 使用网络调试助手时,踩坑
  14. 计算机会计应用实训,大学excel在会计中的应用的实训心得
  15. 百度SiteApp网站打不开,手机站可以这样搞掂
  16. 【笔记】c++ - 正则表达式: GNU Regex Library、PCRE, PCRE++、Boost.Regex
  17. 怎么快速了解一个新的项目
  18. 奶牛与农夫John与oj
  19. dashboard与coredns服务启动发生ContainerCreating的对应方法
  20. 2D转换transform--rotate旋转(日志)

热门文章

  1. SVN(服务端)使用教程
  2. 合宙Luat直播间即将开启,你揭开行业奥秘,让你快人一步。
  3. 前端第三次培训(css选择器)
  4. Kotlin - DSL
  5. relief算法研究
  6. 兼或Inclusive or 和 异或exclusive or
  7. Bugku-杂项部分题目WP
  8. c语言判断奇数偶数_使用C语言宏检查偶数或奇数
  9. 电脑Healthboost注册机
  10. springboot整合druid 监控sql