JXLS 2.4.0系列教程(四)——多sheet是怎麼作到的

注:本文代碼在第一篇文章基礎上修改而成,請務必先閱讀第一篇文章。html

http://www.cnblogs.com/foxlee1024/p/7616987.htmljava

本文也不會過多的講解模板中遍歷表達式的寫法和說明,請先閱讀第二篇文章。數據庫

http://www.cnblogs.com/foxlee1024/p/7617120.htmleclipse

 

  好吧,今天是國慶次日,大清早起來先把文章給寫了吧!工具

  這篇內容主要講解一些如何導出多sheet的報表,我將用一個學生成績表做爲講解案例。多sheet的導出不僅僅是簡單分sheet而已,還加入了分頁的功能。效果先看下圖:this

  Sheet1編碼

 

  Sheet2spa

 

  你們能夠看到,兩個不一樣sheet中,表結構是同樣,可是裏面的學生數據有了個分頁的效果。這是怎麼作到的呢?3d

  這裏,咱們得從模板製做講起,知道了多sheet模板的原理後,寫代碼就輕鬆多了。excel

 

  你們看紅框裏的註釋:

jx:area(lastCell="I8")

jx:each(items="pages", var="page", lastCell="I8" multisheet="sheetNames")

  第一行不用說,是劃定模板區域。第二行看起來是一個很正常的遍歷註釋,可是裏面多了一個multisheet="sheetNames"參數,這個參數就是分sheet的參數。咱們連起來看就是,以整個Excel報表爲一個遍歷輸出的對象,每個sheet就是一條輸出的記錄。簡單的說,就是遍歷一個集合(Listitems,將集合內每個對象輸出到每個獨立的sheet中。

  只要寫上multisheet」屬性JXLS就會自動的分sheet了。那麼還有一個問題,就是multisheet屬性的值sheetNames是什麼意思?這個值是從model中傳來的一個集合(通常用List),裏面存放着每一頁sheet應該起的名字,JXLS在讀取pages進行分sheet遍歷的時候,也會讀取sheetNames進行遍歷給每個sheet更名。

  接下來咱們看看A4單元格的註釋,你們看過第三篇文章應該知道怎麼嵌套循環了,這裏本質上也是一個嵌套,因此A4裏的遍歷標籤的items寫的值就是A1(紅框)遍歷標籤裏的page,而後點上對應的屬性。

  標籤講完了,具體怎麼作呢?咱們打算用一個叫作Page的類做爲分頁的javaBean對象,這個對象裏存放着這一頁應該顯示的學生記錄(List<Student>)和每一頁的頁名(sheet名)。而後將這個page對象放進一個鏈表中,傳給model

 

  上圖就是文章開頭兩張圖片中學生成績分頁的鏈表示意圖。

  接下來咱們開始寫代碼,假設數據庫查詢出來的結果就是一個裝有學生對象的一長串鏈表。那麼咱們從最基本的學生類(student)開始寫起,

public class Student { String id; String name; Integer chinese; Integer math; Integer english; Integer politics; Integer history; Integer geography; public Student(String id, String name, Integer chinese, Integer math, Integer english, Integer politics, Integer history, Integer geography) { super(); this.id = id; this.name = name; this.chinese = chinese; this.math = math; this.english = english; this.politics = politics; this.history = history; this.geography = geography; } public Student() { } /** 如下省略了get/set方法,請自行補全 */ }

  學生類沒什麼好說的,學生的基本信息。接下來咱們寫頁面類(Page),也就是每個sheet應該包含什麼信息。

/** * 該類用來封裝每一頁的數據 */
public class Page { /** * 頁面信息 */
    private String sheetName; // 每一個sheet名字
    private String currentPage; // 當前頁
    private String tolalPage; // 總頁

    /** * 頁面遍歷的數據 List 的泛型自行設置,若是全部數據都來着同一個類就寫那個類, * 不是同一個類有繼承就寫繼承類的泛型,沒有就寫問號。 */
    private List<?> data; public Page(String sheetName, String currentPage, String tolalPage, List<?> data) { super(); this.sheetName = sheetName; this.currentPage = currentPage; this.tolalPage = tolalPage; this.data = data; } public Page() { } /** 如下省略了get/set方法,請自行補全 */ }

  這個類說兩句,屬性data的類型的List,泛型若是你能肯定傳進來的對象就寫上該對象,或者泛型繼承,不能就寫上問號。還有兩個屬性:currentPagetolalPage這算是保留屬性,本篇教程中沒有用到,可是我仍是寫上了,建議同窗們也能夠寫上,由於當前頁或總頁碼能夠日後使用工具標籤時候能夠判斷是否最後一頁。

  接下來是重點了,咱們已經有了從數據庫中查詢的一長串裝有學生對象的鏈表,有了每一頁應該裝什麼數據的頁面對象,接下來咱們要作的就是分頁了。

  把一長串裝有學生對象的鏈表截成一段段的數據,而後裝進page對象中,而後再把一節一節裝有數據的page對象裝進一個新鏈表中。返回給JXSLmodel

  咱們看下分頁代碼:

/** * 此類用於分頁,就是把從數據庫查詢出來的一個完整的List鏈表變成一截一截是數據。 * @author foxlee1024 */
public class DataByPage { static int pagesize = 3; // 每頁記錄數
    
    /** * 根據每頁顯示多少條數據計算總頁數 * @param dataList 數據庫查詢的數據 */
    public static int countPages(List<?> dataList) { int recordcount = dataList.size(); // 總記錄數
        return (recordcount + pagesize - 1) / pagesize; } public static List<Page> byPage(List<?> dataList) { int pagecount; // 總頁數
        int nowDataListPoint = 0; // 讀取到接收的哪一條數據
 pagecount = countPages(dataList); // 計算頁碼
        List<Page> pageList = new ArrayList<Page>(); // 頁面分頁
        for (int i = 0; i < pagecount; i++) { List<Object> pagedata = new ArrayList<Object>(); // 把傳來的數據取出
            while (nowDataListPoint < dataList.size()) { pagedata.add(dataList.get(nowDataListPoint)); nowDataListPoint += 1; if (nowDataListPoint != 0 && nowDataListPoint % pagesize == 0) { break; } } Page page = new Page("page_" + (i + 1), (i + 1) + "", pagecount + "", pagedata); pageList.add(page); } return pageList; } }

  原理就是遍歷傳進來的一長串鏈表,而後根據判斷將他截成一段後裝進鏈表中,而後把鏈表封裝進page類的data屬性裏,接着再把page類裝進鏈表中,而後返回裝有page對象的鏈表。

  好了,全都齊全了,咱們能夠開始寫main方法了。

public class TestMain { public static void main(String[] args) throws Exception { // 模板位置,輸出流
        String templatePath = "E:/template5.xls"; OutputStream os = new FileOutputStream("E:/out5.xls"); List<Student> list = generateData(); // 模擬數據庫獲取數據
        List<Page> page = DataByPage.byPage(list); // 把獲取的數據進行分頁轉換 
        Map<String, Object> model = new HashMap<String, Object>(); model.put("pages", page); model.put("sheetNames", getSheetName(page)); model.put("className", "六年三班"); model.put("teacherComment", "已覈實"); model.put("directorComment", "已覈實"); JxlsUtils.exportExcel(templatePath, os, model); os.close(); System.out.println("完成"); } /** * Excel 的分頁名(頁碼)的封裝 * 此方法用來獲取分好頁的頁名信息,將信息放入一個鏈表中返回 */
    public static ArrayList<String> getSheetName(List<Page> page) { ArrayList<String> al = new ArrayList<String>(); for (int i = 0; i < page.size(); i++) { al.add(page.get(i).getSheetName()); } return al; } /** * 模擬生成數據 */
    public static List<Student> generateData(){ List<Student> list = new ArrayList<Student>(); Student stu1 = new Student("001", "AAA", 10, 20, 30, 40, 50, 60); Student stu2 = new Student("002", "BBB", 20, 30, 40, 50, 60, 70); Student stu3 = new Student("003", "CCC", 30, 40, 50, 60, 70, 80); Student stu4 = new Student("004", "DDD", 40, 50, 60, 70, 80, 90); Student stu5 = new Student("005", "EEE", 50, 60, 70, 80, 90, 100); list.add(stu1); list.add(stu2); list.add(stu3); list.add(stu4); list.add(stu5); return list; } }

  其餘沒要講的,惟一有一個就是須要一個getSheetName() 方法,遍歷獲取每個pagesheeName,而後裝進鏈表中。而後putmodelsheetNames鍵裏。

  模板就按照前邊開頭咱們講解的那個模板寫,接下來咱們運行一下代碼。

  當你看到控制檯打出「完成」,欣喜的打開excel文件時候,你會發現第一頁sheet是空白的......從第二頁開始纔是真正的內容。而後你看到第一頁的sheet名是你模板的sheet名,你就知道確定是JXLS在複製模板時候沒有刪除模板頁面形成的。

 

  這個問題我沒辦法解決,我嘗試過在JxlsUtils中設置JxlsHelper的屬性:jxlsHelper.setDeleteTemplateSheet(true); 然而並沒什麼卵用,不知道是我設置的地方不對,仍是別的緣由。請知道解決方案的同窗務必留言告知一下,萬分感謝!

  雖然沒辦法從根本上解決,可是能夠找到湊活解決的辦法,就是利用POI把多餘的sheet給刪掉,寫一個工具類,代碼以下:

public class DelSheet { /** * 刪除指定的Sheet * @param targetFile 目標文件 * @param sheetName Sheet名稱 */ 
    public static void deleteSheet(String targetFile,String sheetName) { try { FileInputStream fis = new FileInputStream(targetFile); HSSFWorkbook wb = new HSSFWorkbook(fis); fileWrite(targetFile, wb); //刪除Sheet 
 wb.removeSheetAt(wb.getSheetIndex(sheetName)); fileWrite(targetFile, wb); fis.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 寫隱藏/刪除後的Excel文件 * @param targetFile 目標文件 * @param wb Excel對象 * @throws Exception */ 
    public static void fileWrite(String targetFile,HSSFWorkbook wb) throws Exception{ FileOutputStream fileOut = new FileOutputStream(targetFile); wb.write(fileOut); fileOut.flush(); fileOut.close(); } }

  接下來咱們就在main方法中,執行完excel導出的代碼後調用下刪除sheet的語句:

JxlsUtils.exportExcel(templatePath, os, model); os.close(); // 刪除多出來的sheet
DelSheet.deleteSheet("E:/out5.xls", "template"); System.out.println("完成");

  傳入的是excel導出的路徑和要刪除的sheet名字,其實能夠傳入sheet的編號的,可是我以爲傳入名字能夠防止誤刪除。要傳入編號的同窗請自行修改deleteSheet方法,wb.getSheetIndex(sheetName)既能夠接收String也能夠接收Integer,若是我沒有記錯的話。

  咱們再運行一遍代碼看看,咱們就能夠看到開頭的那樣的效果了!

  通常來講,這篇文章到這裏應該就結束了。可是還有一個問題,就是若是我想作每個學生的成績分頁呢?怎麼作?就是一個學生獨佔一個sheet

  我不賣關子了,咱們記得,每個sheet的信息實際上是對應一個page對象的,這個對象裏有一個List<Student> data,因此在一個sheet裏纔可以將這個data取出,交給模板進行遍歷。若是咱們要一個學生獨佔一個sheet,咱們只須要在Page類中加入一個對象類型的屬性就能夠了,在模板中直接取這個對象具體的屬性。這也是我爲何要用page對象的緣由,擴展性高。

public class Page { /** * 頁面信息 */
    private String sheetName; // 每一個sheet名字
    private String currentPage; // 當前頁
    private String tolalPage; // 總頁

    /** * 頁面遍歷的數據 List 的泛型自行設置,若是全部數據都來着同一個類就寫那個類, * 不是同一個類有繼承就寫繼承類的泛型,沒有就寫問號。 */
    private List<?> data; /** * 一頁只保存一我的的信息 */
    private Object onlyOne;   /** 省略構造器和其餘get/set方法 */

    public Object getOnlyOne() { return onlyOne; } public void setOnlyOne(Object onlyOne) { this.onlyOne = onlyOne; }

  接下來咱們修改一下main方法,把本來用來分頁的List<Page> page = DataByPage.byPage(list) 註釋掉。而後新加一句List<Page> page = individual(list)

public static void main(String[] args) throws Exception { // 模板位置,輸出流
        String templatePath = "E:/template5.xls"; OutputStream os = new FileOutputStream("E:/out5.xls"); List<Student> list = generateData(); // 模擬數據庫獲取數據 //List<Page> page = DataByPage.byPage(list); // 把獲取的數據進行分頁轉換
        List<Page> page = individual(list); // 一頁一我的
 Map<String, Object> model = new HashMap<String, Object>(); model.put("pages", page); model.put("sheetNames", getSheetName(page)); model.put("className", "六年三班"); model.put("teacherComment", "已覈實"); model.put("directorComment", "已覈實"); JxlsUtils.exportExcel(templatePath, os, model); os.close(); // 刪除多出來的sheet
        DelSheet.deleteSheet("E:/out5.xls", "template"); System.out.println("完成"); }

  Individual() 方法的代碼以下:

    /** * 將數據獲取的數據封裝成一頁一我的的List */
    public static List<Page> individual(List<Student> list){ List<Page> pages = new ArrayList<Page>(); for(int i = 0; i < list.size(); i++){ Page p = new Page(); p.setOnlyOne(list.get(i)); p.setSheetName(list.get(i).getName()); pages.add(p); } return pages; }

  接收傳進來的List<Student> list鏈表數據,而後遍歷該鏈表,將其封裝Page對象中,而且別忘了設置sheetName。

  模板是這樣的:

 

  模板中不須要兩層循環了,直接在頁面中取page的onlyOne屬性(裝的是Student對象)的值就行了。例如:${page.onlyOne.id}、${page.onlyOne.chinese}。

  行了,執行代碼看一下效果:

  到這裏就真的是結束了,這篇文章內容有點多,主要講了分sheet導出的方法,本質上只是一種的,我先講了比較複雜的,帶有分頁效果的分sheet。而後再講了單純的分sheet。介於代碼比較多,我就把源碼發上了讓你們一塊兒研究。源碼裏的jar包我給刪掉了,要使用就在第一篇文章先下載jar包吧!

順便說一句,前幾篇教程的源碼是不存在的,由於我每一篇都是在原來代碼基礎上改的,其實全部代碼我都發上來了。這篇我是專門開新工程弄的例子。哦,還有一句,家裏eclipse過久不用了,編碼竟然是GBK而不是通用的UTF-8(寫完了才發現),你們看着改吧。

  jar包下載地址(內有官方2.4.0版本,2.4依賴的jar包,klguang demo這裏下載

  本文源碼下載(內有模板,無jar包,請配合上邊jar包使用): 源碼下載

相關文章
相關標籤/搜索