JXLS 2.4.0系列教程(五)——更進一步的應用和頁面邊距bug修復


注:本文代碼建立於前面寫的代碼。不過不看也不要緊。

 

  前面的文章把JXLS 2.4.0 的基本使用寫了一遍,現在講講一些更進一步的使用方法。我只寫一些我用到過的方法,更多的高級使用方法請參考官網。

  http://jxls.sourceforge.net/

 

一、Map的應用

  從一開始,官方dome使用的model中的引用對象一直都是一個javaBean對象。后來我發現其實可以直接往model中放一個map,然后也是直接用“點”的方式在excel中取出數據。

  下面用第四篇文章——多sheet的源碼做演示,我們先修改main方法,加入一個HashMapMap

public static void main(String[] args) throws Exception {
        // 模板位置,輸出流
        String templatePath = "E:/template5_2.xls";
        OutputStream os = new FileOutputStream("E:/out5_2.xls");

        List<Student> list = generateData(); //    模擬數據庫獲取數據
        //List<Page> page = DataByPage.byPage(list); // 把獲取的數據進行分頁轉換
        List<Page> page = individual(list); // 一頁一個人
        
        // 定義一個Map
        Map<String, String> tableInfo = new HashMap<String, String>();
        tableInfo.put("className", "六年三班2");
        tableInfo.put("teacherComment", "已核實2");
        tableInfo.put("directorComment", "已核實2");
        
        Map<String, Object> model = new HashMap<String, Object>();
        model.put("pages", page);
        model.put("sheetNames", getSheetName(page));
        model.put("tableInfo", tableInfo);
        //model.put("className", "六年三班");
        //model.put("teacherComment", "已核實");
        //model.put("directorComment", "已核實");
        
        JxlsUtils.exportExcel(templatePath, os, model);
        os.close();
        // 刪除多出來的sheet
        DelSheet.deleteSheet("E:/out5_2.xls", "template");
        System.out.println("完成");
    }

  把原來的modelput的多注釋掉,然后把定義的Map tableInfo 放入model中。

  我們看看模板中怎么取:

 

  直接取新加入的map的鍵名,再“點上”tableInfo的鍵名。

 

  就這樣輕松的取出來了。Map也是可以直接放進model中的,有時候Map用起來更加靈活,這就要自行選擇了。

 

二、使用工具

  工具的使用先看JxlsUtils類。

public class JxlsUtils{
    public static void exportExcel(InputStream is, OutputStream os, Map<String, Object> model) throws IOException{
        Context context = PoiTransformer.createInitialContext();
        if (model != null) {
            for (String key : model.keySet()) {
                context.putVar(key, model.get(key));
            }
        }
        JxlsHelper jxlsHelper = JxlsHelper.getInstance();
        Transformer transformer  = jxlsHelper.createTransformer(is, os);
        //獲得配置
        JexlExpressionEvaluator evaluator = (JexlExpressionEvaluator)transformer.getTransformationConfig().getExpressionEvaluator();
        //設置靜默模式,不報警告
        evaluator.getJexlEngine().setSilent(true);
        //函數強制,自定義功能
        Map<String, Object> funcs = new HashMap<String, Object>();
        funcs.put("utils", new JxlsUtils());    //添加自定義功能
        evaluator.getJexlEngine().setFunctions(funcs);
        //必須要這個,否者表格函數統計會錯亂
        jxlsHelper.setUseFastFormulaProcessor(false).processTemplate(context, transformer);
    }
    public static void exportExcel(File xls, File out, Map<String, Object> model) throws FileNotFoundException, IOException {
            exportExcel(new FileInputStream(xls), new FileOutputStream(out), model);
    }
    public static void exportExcel(String templatePath, OutputStream os, Map<String, Object> model) throws Exception {
        File template = getTemplate(templatePath);
        if(template != null){
            exportExcel(new FileInputStream(template), os, model);    
        } else {
            throw new Exception("Excel 模板未找到。");
        }
    }
    //獲取jxls模版文件
    public static File getTemplate(String path){
        File template = new File(path);
        if(template.exists()){
            return template;
        }
        return null;
    }    
    // 日期格式化
    public String dateFmt(Date date, String fmt) {
        if (date == null) {
            return "";
        }
        try {
            SimpleDateFormat dateFmt = new SimpleDateFormat(fmt);
            return dateFmt.format(date);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
    // if判斷
    public Object ifelse(boolean b, Object o1, Object o2) {
        return b ? o1 : o2; }
}

  我們看到有一行:funcs.put("utils", new JxlsUtils());    //添加自定義功能

//函數強制,自定義功能
Map<String, Object> funcs = new HashMap<String, Object>();
funcs.put("utils", new JxlsUtils());    //添加自定義功能
evaluator.getJexlEngine().setFunctions(funcs);

  這行代碼的意思就是設置一個工具類JxlsUtils(),在excel中的引用為utils 。當你在excel中使用 utils 時就會調用JxlsUtils() 類(可以是其他的類)的對應方法。

  我們來看下模板怎么寫:

 

  在模板中,你可以這樣調用工具類:

${utils:ifelse(page.currentPage == page.tolalPage,"最后一頁","")}

  JXLS會自動找到utils這個鍵對應的值 new JxlsUtils() ,然后在JxlsUtils類中找對應的ifelse方法,然后根據方法接收的參數自動把你在excel寫的參數傳進去,再把方法的返回值顯示在excel中。

  還記得我前面保存進Page中的當前頁碼和總頁碼這兩個屬性嗎?現在就是使用使用他們了。用他們可以判斷現在的sheet是不是最后一頁,最后一頁可以顯示一些別的數據。一般說ifelse這個已經夠用了,但是如果你有更加復雜的操作,你也可以自行定制自己需要的工具類。

 

三、JXLS 是支持模板多個sheet輸出數據的

  這句話很奇怪,但是我一下子沒想出別的表達。這個多sheet不是前面我們講的分sheet,是說你模板有本身就已經有很多個sheet了,每個sheet顯示的表都不一樣,不用擔心,你存放進model中的數據是可以跨sheet的。只要你想取出就能取出,這個我就不演示了。

 

四、頁面邊距bug

  這個bug一般人是真沒有發現的,但是對我來說就是差點要了老命....我們公司項目導出的excel報表比較大,一張A3紙都只是勉勉強強夠打印。所以平常邊距要設定得很小,這個邊距的bug就是你在模板中設定的頁面邊距並不會拷貝進你導出的文件的。

  你可能試驗了一下說,沒有啊,我的沒問題啊。我說的不會拷貝僅是在分頁分sheet導出時候出現的問題。我舉個例子,你在模板中設定了頁面邊距,然后導出,JXLS會先把你的模板復制進導出的文件中,然后建立一個新的sheet,然后在根據模板的設定把內容拷貝進新sheet中,然后再建立新sheet不斷循環。

  問題就出現在把模板拷進新sheet的過程中,如果你改變了模板的頁面邊距,再進行一個分sheet導出,不要用POi刪除導出文件第一頁空的模板sheet,你查看第一頁的頁邊距你發現是已經修改過的頁邊距,而你查看后面其他sheet的頁邊距就會是excel默認的頁邊距了。

  不明白不要緊,你只要知道我已經解決了就好(新版本2.4.2我不知道作者有沒有修復)。

 

  問題就出現在這個PoiUtil的文件上,我們打開看看,看到copySheetProperties(Sheet src, Sheet dest)這個方法,這個方法就是拷貝sheet屬性的。

public static void copySheetProperties(Sheet src, Sheet dest){
        dest.setAutobreaks(src.getAutobreaks());
        dest.setDisplayGridlines(src.isDisplayGridlines());
        dest.setVerticallyCenter(src.getVerticallyCenter());
        dest.setFitToPage(src.getFitToPage());
        dest.setForceFormulaRecalculation(src.getForceFormulaRecalculation());
        dest.setRowSumsRight(src.getRowSumsRight());
        dest.setRowSumsBelow( src.getRowSumsBelow() );
        //增加頁邊距保存
 dest.setMargin(Sheet.TopMargin, src.getMargin(Sheet.TopMargin)); dest.setMargin(Sheet.LeftMargin, src.getMargin(Sheet.LeftMargin)); dest.setMargin(Sheet.RightMargin, src.getMargin(Sheet.RightMargin)); dest.setMargin(Sheet.BottomMargin, src.getMargin(Sheet.BottomMargin));
        
        copyPrintSetup(src, dest);
    }

  這個方法內少寫了對sheet頁邊距的拷貝,加上就行。加上后怎么放回包里?你可以自行打包....你也可以在你的項目目錄上建立一個與PoiUtil類一模一樣的路徑,然后在里面放上修改后的類。Java在找包時候會優先找你寫的路徑,這樣就覆蓋掉了包路徑了。

 

  在src的根目錄上,和com同級。

  好了,這一篇文章就寫到這里,寫這篇文章時候我是懶的,我懶得再去做案例驗證了,特別是最后的那個頁邊距的bug,我在當初我在網絡上根本找不到和我一樣的問題。我也是花了些功夫才找到這個原因的。本來我應該做下案例把這個bug講清楚些,但是我寫到這里我已經有些懶了.....說白了這個bug就是只有在分sheet導出時候才會出現的問題,能自己復現的就自己研究下,不能的,知道怎么解決了就行了。

  下載PoiUtil類: 點這里下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM