1. 程式人生 > >基於freemarker實現excel的模板匯出

基於freemarker實現excel的模板匯出

這裡簡單介紹一下使用freemarker的好處,適用於複雜的模板匯出功能,並且還可以保持原有模板的一些方法。 

第一步:新增依賴

我的專案是基於maven,所以在pom.xml中新增一下依賴就可以了:

<dependency>  
   <groupId>org.freemarker</groupId>  
   <artifactId>freemarker</artifactId>  
   <version>2.3.20</version>  
</dependency>

第二步:把excel另存為xml

在這裡我要說一下,excel在另存為xml時裡面的圖片會丟失,它並不會像word那樣生成一個佔位符。所以我這個匯出的excel是沒有圖片的。言歸正傳,這就是我另存為的xml的樣例,其中row為行,cell為單元格

第三步:把要插入的資料使用freemarker的標籤----${}替換

看到這裡的${},是不是覺得它很像jstl標籤,它的用法確實和jstl差不多,jstl是獲取session中的值,而他是獲取dataMap中的值;而且都是可以迴圈的便利的,它的便利標籤就是

<#list list as item><#--其中list為你後臺在dataMap中的key-->


    <#if item.detaildata??><#--判斷是否為空,如果不為空則顯示內容-->

<#--${item.detaildata}是獲取list的子元素的值-->
 <Cell ss:StyleID="s37"><Data ss:Type="String">${item.detaildata}</Data></Cell>
   <#else>
<#--否則顯示空的單元格-->
   <Cell ss:StyleID="s37"/>
  </#if>
  </#list>

我想很多人還是不清楚我講的,可能確實是我的語言組織能力不好,不多說直接上截圖,簡潔明瞭

第四步:把xml的字尾名改成ftl,並且在webapp中新建template,把xxx.ftl,放到裡面

好了基本的操作已經做完了,下面就是後臺程式碼了。因為我的專案是MVC的設計理念並沒有使用任何框架,所以把後臺分為了四個層面:資料庫物件序列化(dto),資料庫訪問層(DAO),資料處理層(service),控制層(servlet),算了還是用一個簡單的例子吧,我開發的專案可能就要從頭講了。廢話不多說先上service的程式碼,是一個封裝好的類,可以直接呼叫的:

public class ExportExcelService {
    public static void export(String templatePath, Map<String, Object> dataMap,
            String buildFile, String newName,HttpServletRequest request,HttpServletResponse response) {
        try {
            ServletContext application = request.getSession().getServletContext();
            Configuration configuration = new Configuration();
            configuration.setDefaultEncoding("utf-8");
            String path = application.getRealPath("template");

// 此處是本類Class.getResource()相對於模版檔案的相對路徑
            configuration.setDirectoryForTemplateLoading(new File(path));
            Template template = null;
            File outFile = new File(buildFile);
            Writer writer = null;
            template = configuration.getTemplate(templatePath);
            template.setEncoding("utf-8");
            writer = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(outFile), Charset.forName("utf-8")));// 此處為輸
            template.process(dataMap, writer);
            writer.flush();
            writer.close();
            // return true;
            // 設定response的編碼方式
            response.setContentType("application/x-msdownload");
            // 設定附加檔名
            response.setHeader("Content-Disposition", "attachment;filename="
                    + new String(newName.getBytes("utf-8"), "iso-8859-1"));
            // 讀出檔案到i/o流
            FileInputStream fis = new FileInputStream(outFile);
            BufferedInputStream buf = new BufferedInputStream(fis);
            byte[] b = new byte[1024];// 相當於我們的快取
            long k = 0;// 該值用於計算當前實際下載了多少位元組
            // 從response物件中得到輸出流,準備下載
            OutputStream myout = response.getOutputStream();
            // 開始迴圈下載
            while (k < outFile.length()) {
                int j = buf.read(b, 0, 1024);
                k += j;
                // 將b中的資料寫到客戶端的記憶體
                myout.write(b, 0, j);
            }
            // 將寫入到客戶端的記憶體的資料,重新整理到磁碟
            myout.flush();
            myout.close();
        } catch (Exception e) {
            e.printStackTrace();
            // return false;
        }
    }
}

然後我們在servlet中呼叫這個類中的方法,重點就看我標黃的程式碼,這個是自己生成的list,而不是通過其他程式碼生成的,標藍色的則是單一的鍵值對,紅色程式碼為如果匯出的資料不是模板的7天資料,則自動補充不足的空資料,以完成模板顯示空單元格如下:

@WebServlet("/excelExport")
public class exportExcelServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uz=new String(URLDecoder.decode(req.getParameter("uz"),"utf-8"));
        ReimburseService res=new ReimburseService();
        String[] data=uz.split(",");
        String uname=uz.split(",")[1];
        String weeknumber=uz.split(",")[0];
        Model model=res.querydetail(uname, weeknumber);
        Map<String, Object> dataMap = new HashMap<String, Object>();
        dataMap.put("username",uz.split(",")[2]);
        dataMap.put("writedata",uz.split(",")[3]);
        dataMap.put("appartment",uz.split(",")[4]);

        List list=(List) model.getData();
        for(int i=list.size();i<7;i++){
            ReimburseDetail rd=new ReimburseDetail();
            rd.setDetaildata("");
            rd.setHotelfare("");
            rd.setLocation("");
            rd.setLongfare("");
            rd.setOtherfare("");
            rd.setShortfare("");
            rd.setProject("");
            rd.setFaredescription("");
             list.add(rd);
        }

        //對list進行排序
        for(int i=0;i<list.size();i++){
            ReimburseDetail rdl=(ReimburseDetail)list.get(i);
            try {
                if(!rdl.getDetaildata().equals("")){
                    int b=dayForWeek(rdl.getDetaildata());
                    if(b-1!=i){
                        Object objA= list.get(i);
                        list.set(i, list.get(b-1));
                        list.set(b-1, objA);
                    }
                }            
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        dataMap.put("list",list);
        List ls=new  ArrayList();
        String[] num={"A","B","C","D","E","F","G"};
        String[] Formula={"-9","-10","-11","-12","-13","-14","-15"};
        for(int i=0;i<num.length;i++){
            Map<String,Object> map = new HashMap<String,Object>();  
            map.put("num",num[i]);
            ReimburseDetail r=(ReimburseDetail)list.get(i);
            map.put("faredescription",r.getFaredescription());
            map.put("Formula", Formula[i]);
            map.put("col", i+1);
            ls.add(map); 
        }
        dataMap.put("ls",ls);

        ServletContext application = req.getSession().getServletContext();
        /*********輸出圖片未解決********************/
        String a=application.getRealPath("imgs");
        String img = getImageStr(a+"/ceit.png");
        dataMap.put("image", img);
        /***********************************/
        String  templateName = "excel.ftl";   //模板名稱
        java.text.DateFormat format1 = new java.text.SimpleDateFormat("yyyy-MM-dd");
        String exportexcel = application.getRealPath("template")+format1.format(new Date())+ ".xls";
        ExportExcelService.export(templateName, dataMap, exportexcel, "每週報銷單("+format1.format(new Date())+").xls",req,resp);
        
    }
    /***************獲取日期是星期幾*************************/
    public  static  int  dayForWeek(String pTime) throws  Exception {   
        SimpleDateFormat format = new  SimpleDateFormat("yyyy-MM-dd" );   
         Calendar c = Calendar.getInstance();   
         c.setTime(format.parse(pTime));   
         int  dayForWeek = 0 ;   
         if (c.get(Calendar.DAY_OF_WEEK) == 1 ){   
          dayForWeek = 7 ;   
         }else {   
          dayForWeek = c.get(Calendar.DAY_OF_WEEK) - 1 ;   
         }   
         return  dayForWeek;   
        }
    /**************將圖片轉成base64********************/
    public String getImageStr(String imgFile) {
        InputStream in = null;
        String da="";
        byte[] data = null;
        try {
            in = new FileInputStream(imgFile);
            data = new byte[in.available()];
            in.read(data);
            in.close();
             da=new String(Base64.encodeBase64(data),"UTF-8");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
        e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return da;

    }

}
好了,文章寫的這裡就圓滿結束了。唯一的遺憾就是圖片匯出還沒有解決,等解決了再來補充,當然如果是匯出word模板是可以匯出圖片的因為再把帶有圖片的word轉xml時會生成這個標籤------》<w:binDataw: name="wordml://03000001.png" xml:space ="preserve">${image}</w:binData><v:shape id="_x0000_i1025" type="#_x0000_t75" style="width:414.75pt; height:319.5pt">  <v:imagedata src="wordml://03000001.png" o:title=""/></v:shape>關於這個網上有很多,可以多去逛逛。