首页 > 代码库 > freemarker+Jfreechart生成Word文档(含图片)

freemarker+Jfreechart生成Word文档(含图片)

     这几天再弄一个报表,要统计一些信息最终的部分展示结果如下:技术分享

基本工具freemarker,jfreechart

工程的部分结构如下

技术分享

与生成Word有关的类主要有FreemarkerConfiguration和WordGenerator代码如下:

import com.bqs.ares.common.utils.CommonUtils;
import freemarker.template.Configuration;

import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by lenovo on 2016/9/27.
 */
public class FreemarkerConfiguration {
    private static Logger log = LoggerFactory.getLogger(FreemarkerConfiguration.class);
    private final static String filepath = "/freemarkerTemplate";
    private static Configuration configuration=null;
    public static Configuration getConfiguration(){
        if(configuration==null){
            configuration=new Configuration();
            try {
                configuration.setDirectoryForTemplateLoading(new File(CommonUtils.class.getResource(filepath).getFile()));
            }catch (Exception e){
                log.error(e.getMessage());
            }
        }
        return  configuration;
    }
}

import com.bqs.risk.dvp.common.PDFUtils.freemarker.FreemarkerConfiguration;
import freemarker.template.Configuration;
import freemarker.template.Template;

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

/**
 * Created by lenovo on 2016/10/9.
 */
public class WordGenerator {
    /**
     * Generate html string.
     *
     * @param template   the name of freemarker teamlate.
     * @param variables  the data of teamlate.
     * @return htmlStr
     * @throws Exception
     */
    public static void generate(String template, Map<String,Object> variables, String htmlName) throws Exception{
        String basePath=HtmlGenerator.class.getResource("/").getPath()+"/freemarkerTemplate/word/";
        Configuration config = FreemarkerConfiguration.getConfiguration();
        config.setDefaultEncoding("UTF-8");
        Template tp = config.getTemplate(template);
        tp.setEncoding("UTF-8");

        String htmlPath=basePath+htmlName+".doc";
        File file = new File(htmlPath);
        if (!file.exists())
            file.createNewFile();
        Writer out = new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream(file), "utf-8"));
        tp.process(variables, out);
        out.flush();
        out.close();
    }
}

用jfreeChart生成折线图和饼图的代码如下:

import com.bqs.risk.dvp.common.InfoPoint;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.ui.TextAnchor;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.*;
import java.util.List;

/**
 * Created by lenovo on 2016/9/28.
 */
public class JfreeChartUtils {
    private static Map<String,String> relationNameMap=null;
    private static DefaultCategoryDataset createDataset(int[] data) {
        DefaultCategoryDataset linedataset = new DefaultCategoryDataset();
        // 曲线名称
        String series = "时间-次数";  // series指的就是报表里的那条数据线
        //因此 对数据线的相关设置就需要联系到serise
        //比如说setSeriesPaint 设置数据线的颜色
        // 横轴名称(列名称)
        String[] time = new String[24];

        for (int i = 0; i < 24; i++) {
            time[i] = i + "";
        }
        //添加数据值
        for (int i = 0; i < data.length; i++) {
            linedataset.addValue(data[i],  //值
                    series,  //哪条数据线
                    time[i]); // 对应的横轴
        }
        return linedataset;

    }

    //生成事件统计图
    public static String createChart(String eventTypevalue, int[] data, String imageName) {
        String returnImagePath = "";
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.MONTH, -1);
        if (data =http://www.mamicode.com/= null || data.length <= 0) {>图片得到之后就是生成Word了,基本思路是先用Word做好模板,另存为xml,然后使用freemarker标签来替换,最后改成ftl文件就是模板,图片也只是替换那一大堆编码而已
 <w:pict>
              <w:binData w:name="wordml://03000003${imagePath_index}.png" xml:space="preserve">${imagePath}</w:binData>
                <v:shape id="_x0000_i1028" type="#_x0000_t75" style="width:440pt;height:440pt">
                    <v:imagedata src=http://www.mamicode.com/"wordml://03000003${imagePath_index}.png" o:title="6350.tmp"/>>

但这里确实花了我很多时间,主要的坑如下:

     1.freemarker标签的使用:尤其要注意是否为空的情况所以对于list还是map最好都加上这一句:

<#if infoLocationList??&&infoLocationList?size gt 0>
是否存在,至于具体的循环遍历网上有不再赘述

     2.Word模板另存为xml文件时一定要注意,如果是较高一点的版本有两种格式xml和2003xml这个在处理图片时有较大差别,我是使用2003xml版本(高版本的在处理图片时有错误),它在处理的时候需要将图片转成base64码具体代码如下:

 private String getImageStr(String imagePath) {
        String imgFile = imagePath;
        InputStream in = null;
        byte[] data = http://www.mamicode.com/null;>
  3.多张图片插入时会遇到图片重复记得改name和src,从List中取出数据并且实现插入多张图片:

<w:p wsp:rsidR="00C46D75" wsp:rsidRDefault="00C46D75" wsp:rsidP="00AC571C"/>
        <#if infoLocationList??&&infoLocationList?size gt 0>
            <w:p wsp:rsidR="0051625F" wsp:rsidRDefault="007F46BC" wsp:rsidP="00AC571C">
          <w:r>
            <w:rPr>
              <w:rFonts w:hint="fareast"/>
              <wx:font wx:val="宋体"/>
            </w:rPr>
            <w:t>半年内手机号、身份证号、IP、GPS归属地信息分布</w:t>
          </w:r>
        </w:p>
            <#list infoLocationList as imagePath>
                <w:p wsp:rsidR="007F46BC" wsp:rsidRDefault="00E75FA4" wsp:rsidP="00AC571C">
          <w:r>
            <w:tab/>
          </w:r>
          <w:r wsp:rsidR="00946545" wsp:rsidRPr="00946545">
            <w:rPr>
              <w:noProof/>
            </w:rPr>
            <w:pict>
              <w:binData w:name="wordml://03000003${imagePath_index}.png" xml:space="preserve">${imagePath}</w:binData>
                <v:shape id="_x0000_i1028" type="#_x0000_t75" style="width:440pt;height:440pt">
                    <v:imagedata src=http://www.mamicode.com/"wordml://03000003${imagePath_index}.png" o:title="6350.tmp"/>>  4.如果模板太大转成的xml文件太大,看着是密密麻麻的一大片根本无法进一步处理,建议使用IDE进行代码格式化,想eclipse等等一般都可以,如果发现工具也无法格式化可以直接百度xml格式化,然后会有在线的工具

  5.有时候会遇到成功生成Word文档但是无法打开的情况,这时候可以根据错误提示用文本编辑工具来具体到哪一行去看看,如果遇到什么结束元素标签名称与开始标签名称不匹配多半是ftl中标签配对有问题(这个有时候即使没有改过也会出错,所以最好借助工具好好补齐标签)

 6.乱码问题,主要是在Linux服务器上会出现,具体方法网上也有

以上是自己的一些经验之谈,更多是在整个流程以及自己所遇到的坑上,具体说每一步怎么处理网上都会有

freemarker+Jfreechart生成Word文档(含图片)