将多个Echarts图片导入word文档

  大家好,今天跟大家分享一个近期项目中遇到的问题以及解决的方案。我是抛砖引玉,大家如果有更好的方案,欢迎讨论;如果正好你也遇到这样的问题,可以参考一下这个方案。


功能需求

  1. 一键导出word报告
  2. 需要将多个echarts图片存入word


环境

  1. jdk8及以上
  2. 前端使用echarts或其他canvas类型图表
  3. freemarker
  4. tomcat


思路

1. 将echarts图片转为base64编码,以post方式传参到后台。
  这里有一个问题,tomcat对于post参数有默认最大长度(2M),echarts图形生成的base64编码是相当大的,一个有几百K大小是正常的,所以当有10个echarts图片要导出时,就会超过tomcat的限制。
有两个解决方案:
  1. 修改tomcat的server.xml配置
  2. 当echarts初始化完成后,将每个echarts图片的base64编码通过ajax方式单独保存(记得要有编号id),推荐保存到redis(保存到mysql也是一样的,不过redis的读写比较快)。

  用过echarts、highcharts以及其他canvas做数据可视化图表的朋友应该都知道,canvas图片可以通过canvas.toDataURL("image/png")方法将图片转化为base64编码。

其实echarts也有类似的方法,代码如下:

//   myChart代表echarts实例
var dataUrl = myChart.getDataURL({    
    // 导出的格式,可选 png, jpeg    
    type : "png",    
    // 导出的图片分辨率比例,默认为 1。    
    pixelRatio : 2,    
    // 导出的图片背景色,默认使用 option 里的backgroundColor    
    backgroundColor : '#fff' 
  }); 
  dataUrl = dataUrl.substring(22); 
  // savePngBase64(dataUrl,parsenum);

生成的base64编码部分如下(完整的数据太长了,这里只展示部分,大家看格式即可):

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAArcAAAPZCAYAAAALFALpAAAgAElEQVR4XuxdB5gURdp+Z2ZzzrsE
ERVEDwMgoh6CKJhQD3P2wIThDOedOVxQ74ynvzmiICeeOWAGBUUEEQEjAipB0i6b887uzvzPW7s1
1vZ2T/dMz8IuVD3PPYc7VdVVb1V3v/3V+32fZ8RBo4Llj58JXaJDIOfiGVi0YJ7HrrUVzv41xSi+
9mnRvPDuC5DQv9C0q8ZlP2P9xHuROKhvp3ryt5Tf/w6ph+2LLbe/gJQD90DBvybBmxCP4r9PR93H
y1Dwj7ORccJItFbWovjGqaif/z3yrj4ZWeeMFdeUY2lasR6ZZ4xBXEG2+HuwyY/aD7+C/5fNSD9m
BBIG9Gn7e0ur6IPXz/nTcciZPF78e+OlDyP96P2RcepolNwyDb7cDBTecR6CLS2omjFXjM1XkCXm
7U1PQeGd56Hpx19RN2sJEvfuj8xTRnfCwCnOduugf489AvtNGL+66tbj+sueWyvrUHzDM217+o7z

2.制作word模板文件demo.ftl,将获得的base64编码以参数的形式带入demo.ftl文件
  demo.ftl文件可以通过word制作,将word另存为.xml文件,然后将后缀名改为.ftl即可。flt文件引用base64编码的位置信息一般是这样的,其中${img}代表base64编码部分,具体代码如下:

  1. ftl文件配置部分
<#list imgs as img>
  <w:drawing>
    <wp:inline distT="0" distB="0" distL="0" distR="0" wp14:anchorId="19DBB609" wp14:editId="34BC3657">
      <wp:extent cx="5259600" cy="7423200"/>
      <wp:effectExtent l="0" t="0" r="0" b="0"/>
      <wp:docPr id="1" name="图片 1"/>
      <wp:cNvGraphicFramePr>
        <a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/>
      </wp:cNvGraphicFramePr>
      <a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
        <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
          <pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
            <pic:nvPicPr>
              <pic:cNvPr id="0" name="Picture 1"/>
                <pic:cNvPicPr>
                  <a:picLocks noChangeAspect="1" noChangeArrowheads="1"/>
                </pic:cNvPicPr>
               </pic:nvPicPr>
             <pic:blipFill>
             <a:blip r:embed="rId4${img_index+1}" cstate="print">
                 <a:extLst>
                     <a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
                       <a14:useLocalDpi xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/>
                     </a:ext>
                  </a:extLst>
               </a:blip>
            <a:srcRect/>
            <a:stretch>
                <a:fillRect/>
            </a:stretch>
            </pic:blipFill>
            <pic:spPr bwMode="auto">
                <a:xfrm>
                    <a:off x="0" y="0"/>
                    <a:ext cx="5259600" cy="7423200"/>
                </a:xfrm>
               <a:prstGeom prst="rect">
                   <a:avLst/>
                   </a:prstGeom>
                   <a:noFill/>
                     <a:ln>
                   <a:noFill/>
                   </a:ln>
                 </pic:spPr>
               </pic:pic>
             </a:graphicData>
           </a:graphic>
         </wp:inline>
       </w:drawing>
   </#list>

<pkg:part pkg:name="/word/_rels/document.xml.rels" pkg:contentType="application/vnd.openxmlformats-package.relationships+xml" pkg:padding="256">
    <pkg:xmlData>
        <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
           <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Target="webSettings.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="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>
           <Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/>
           <#list imgs as img>
               <Relationship Id="rId4${img_index+1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image${img_index+1}.png"/>
           </#list>
        </Relationships>
    </pkg:xmlData>
</pkg:part>

<#list imgs as img>
  <pkg:part pkg:name="/word/media/image${img_index+1}.png" pkg:contentType="image/png" pkg:compression="store">
    <pkg:binaryData>
    ${img}
    </pkg:binaryData>
  </pkg:part>
</#list>
  1. 参数带入ftl文件Java部分
// 对应上面说的方案:
// 如果是base64编码挨个存库,那么这里需要通过某个关联id去数据库中取base64编码
// 如果是直接通过前端传参,可以通过request.getParameter("dataUrls")方式获取
@RequestMapping("/downloadWord")
public void downloadWord(HttpServletRequest request, HttpServletResponse response) {
    String filename = "demo";
    final String SPLIT = "###";
    String urls = request.getParameter("urls");
    String[] urlArr = urls.split(SPLIT);
    List<String> imgs = Arrays.asList(urlArr);
    Map data = new HashMap();
    data.put("imgs",imgs);
    try {
      WordUtils.exportWord(request, response, data, "demo.ftl");
    } catch (Exception e) {
      e.printStackTrace();
    }
}

WordUtils 工具类(该工具类参考了网上的一些资料)

public class WordUtils {
    
  public static void exportWord(HttpServletRequest request, HttpServletResponse response, 
            Map map,String ftlFile) throws Exception {  
        Configuration configuration = new Configuration();
        configuration.setDefaultEncoding("utf-8"); 
        String name = URLDecoder.decode(request.getParameter("docName"), "UTF-8");
        File file = null;  
        InputStream fin = null;  
        ServletOutputStream out = null;  
        try {  
            configuration.setDirectoryForTemplateLoading(new File(templateFolder)); 
            Template freemarkerTemplate = configuration.getTemplate(ftlFile);  

            // 调用工具类的createDoc方法生成Word文档  
            file = createDoc(map,freemarkerTemplate);  
            fin = new FileInputStream(file);  
  
            response.setCharacterEncoding("utf-8");  
            response.setContentType("application/msword");  
            // 设置浏览器以下载的方式处理该文件名  
            String fileName = name+ ".doc";
            
            // Header Manipulation漏洞,过滤特殊字符
            String regex = "[`~!@#$%^&*()\\+\\=\\{}|:\"?><【】\\/r\\/n]";
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(fileName);
            if(matcher.find()) {
                fileName = matcher.replaceAll("");
            }
            
            response.setHeader("Content-Disposition", "attachment;filename="  
                    .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));  
  
            out = response.getOutputStream();  
            byte[] buffer = new byte[512];  // 缓冲区  
            int bytesToRead = -1;  
            // 通过循环将读入的Word文件的内容输出到浏览器中  
            while((bytesToRead = fin.read(buffer)) != -1) {  
                out.write(buffer, 0, bytesToRead);  
            }  
        } catch (Exception e){
            e.printStackTrace();
        } finally {  
            if(fin != null) fin.close();  
            if(out != null) out.close();  
            if(file != null) file.delete(); // 删除临时文件  
        }  
    }  

}

3.最后导出结果

QQ图片20200316103120.png

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容