1. 程式人生 > >專案中使用POI匯出Excel的一些心得(包含複雜表頭和動態頁數與列數)

專案中使用POI匯出Excel的一些心得(包含複雜表頭和動態頁數與列數)

最近使用poi做Excel匯出較多,應用場景也各種各樣,導致基本都是按需求自己手動拼的Excel,網上的通用工具類大多不適合業務需要,簡單記錄下來以免下次用到。

通用版(根據jsp上的表格內容動態生成Excel)

工具類

@SuppressWarnings("deprecation")
    public static void fromHtmlTable(String tableHtml,String fileName,OutputStream outputStream){
        HSSFWorkbook wb = null;
        try{
            wb = new
HSSFWorkbook(); HSSFSheet sheet = wb.createSheet("sheet1"); TableRow[] rows = parserTable(tableHtml); int rowIndex = 0; Boolean hasCheckbox = false; Integer checkboxTdIndex = -1; //表頭樣式 HSSFCellStyle thStyle = createCellStyle(wb, true
); //表體樣式 HSSFCellStyle tdStyle = createCellStyle(wb, false); //儲存跨行時需要的列索引 String[][] aa = new String[rows.length][MAX_COL]; for(int trIndex = 0 ;trIndex < rows.length;trIndex++){ TableRow tableRow = rows[trIndex]; HSSFRow row = sheet.createRow((short
)rowIndex); int cellIndex = 0; //判斷是否為表頭,包含"th" if(tableRow.hasHeader()) { //整行表頭的樣式 //row.setRowStyle(thStyle); TableHeader[] heads = tableRow.getHeaders(); for(int tdIndex = 0 ;tdIndex< heads.length;tdIndex++){ //如果第x列為複選框,則跳過 if(heads[tdIndex].getStringText() != null && heads[tdIndex].getStringText().contains("checkbox")){ hasCheckbox = true; checkboxTdIndex = tdIndex; continue; } //獲取rowspan String rowspanstr = heads[tdIndex].getAttribute("rowspan"); Integer rowspan = Integer.parseInt(StringUtils.isBlank(rowspanstr)?"1":rowspanstr); //獲取colspan String colspanstr = heads[tdIndex].getAttribute("colspan"); Integer colspan = Integer.parseInt(StringUtils.isBlank(colspanstr)?"1":colspanstr); //被跨行後,需跳過 while(aa[trIndex][cellIndex]!=null) { cellIndex ++; } HSSFCell cell = row.createCell((short) cellIndex); cell.setCellStyle(thStyle); //跨行 if ( rowspan != 1) { //合併單元格 sheet.addMergedRegion(new Region (rowIndex,(short)cellIndex, rowIndex+rowspan-1,(short)cellIndex)); for (int i=rowIndex+1 ; i<=rowIndex+rowspan-1 ; i++) { aa[i][cellIndex] = "1"; } } //跨列 if (colspan != 1) { //合併單元格 sheet.addMergedRegion(new Region (rowIndex, (short)cellIndex,rowIndex, (short)(cellIndex+colspan-1))); } cellIndex = cellIndex+colspan-1; cell.setCellValue(heads[tdIndex].getStringText()); cellIndex++; } } else { TableColumn[] cls=tableRow.getColumns(); for(int tdIndex = 0 ;tdIndex< cls.length;tdIndex++) { //如果第x列為複選框,則跳過 if(hasCheckbox && tdIndex == checkboxTdIndex){ continue; } HSSFCell cell = row.createCell((short) cellIndex); TableColumn tc = cls[tdIndex]; String cellValue = tc.getStringText(); cellValue = cellValue.replaceAll("<[^>]*>", ""); cell.setCellValue(cellValue); cell.setCellStyle(tdStyle); cellIndex++; } } rowIndex++; } } catch (Exception e) { e.printStackTrace(); } if(wb != null){ try { wb.write(outputStream); } catch (IOException e) { e.printStackTrace(); } } }
private static TableRow[] parserTable(String tableHtml) throws ParserException {
        Parser ps = new Parser(tableHtml);
        NodeFilter nodeFilter=new TagNameFilter("table");
        NodeList list=ps.extractAllNodesThatMatch(nodeFilter);
        TableTag  node=(TableTag)list.elementAt(0) ;
        TableRow[]  rows= node.getRows();
        return rows;
    } 

jsp

除了應有的表格內容外

<form id='form1_1' name="form1_1" action="${ctx}/chartTools/swf/saveToExecl3.jsp" method='post' style="display: hidden">
    <input type='hidden' name='type' id='type'/>
    <input type='hidden' name='sfhm' id='sfhm'/>
    <input type='hidden' name='objectid' id='objectid' value=""/>
</form>

saveToExecl3.jsp

<%!private String toUtf8(String s){
        StringBuffer sb = new StringBuffer();
        for (int i=0;i<s.length();i++){
          char c = s.charAt(i);
          if (c >= 0 && c <= 255){sb.append(c);}
          else{
              byte[] b;
              try { b = Character.toString(c).getBytes("utf-8");}
              catch (Exception ex) {
                  System.out.println(ex);
                  b = new byte[0];
              }
              for (int j = 0; j < b.length; j++) {
                  int k = b[j];
                  if (k < 0) k += 256;
                  sb.append("%" + Integer.toHexString(k).toUpperCase());
              }
          }
        }
        return sb.toString();
    }%>
<%
    request.setCharacterEncoding("utf-8");
    String reqString =URLDecoder.decode(request.getParameter("content"),"UTF-8");
    String filename = request.getParameter("filename");
try{
    out.clear(); 
    out = pageContext.pushBody(); 
    OutputStream outputStream = response.getOutputStream();
    response.setContentType("application/vnd.ms-excel;utf-8");
    response.addHeader("Content-Disposition", "attachment; filename=\"" + toUtf8(filename)  +   ".xls\"");
    ExportToExcel.fromHtmlTable2(reqString,filename,outputStream);
    //outputStream.flush();
}catch(Exception e){
}
%>

主要就這兩個部分,請求方式按自己的來,表名需要轉化編碼,th和td會對應拼到Excel上。

手動拼裝複雜表頭

有專案需要的Excel表頭為多個單元格合併顯示的情況,這種情況因為表頭固定,所以也不會要求動態生成表頭,因此我都是直接在後臺拼裝好表頭,內容部分按通用方式生成這樣的處理方式。

基本操作

HSSFCell title = sheet.createRow(0).createCell(0);
title.setCellType(HSSFCell.CELL_TYPE_STRING);
title.setCellStyle(cs);
title.setCellValue(filename.trim());
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 10));

createRow為建立行,createCell為建立列,建議一行一行拼接,不然很容易出問題。
setCellType為設定樣式,具體樣式對應程式碼可上網查詢,setCellValue為設定單元格內容。
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 10));為合併單元格操作方式,
四個引數分別是:起始行,起始列,結束行,結束列 。
這裡將第一行前11列合併,用來顯示錶名。

難處理的地方

基本操作其實就能滿足合併單元格的要求了,可如果表頭較多,有時不止是列需要合併,行也需要合併,這種時候後臺寫起合併來就容易轉的暈乎乎的,行和列本就容易弄錯,這裡介紹一些自己的經驗。

  1. 優先一行一行處理
    尤其是生成時,先生成行,一行一行處理,包括賦值和合並單元格
//第三行 列標題 
HSSFRow row2 = sheet.createRow(2);
HSSFCell title = row2.createCell(1);
title.setCellType(HSSFCell.CELL_TYPE_STRING);
title.setCellStyle(cs);
title.setCellValue("人數1");
HSSFCell title2 = row2.createCell(7);
title2.setCellType(HSSFCell.CELL_TYPE_STRING);
title2.setCellStyle(cs);
title2.setCellValue("人數2");
sheet.addMergedRegion(new CellRangeAddress(2, 2, 1, 5));
sheet.addMergedRegion(new CellRangeAddress(2, 2, 7, 10));
  1. 合併單元格的賦值要對應未合併前的單元格位置
    簡單說,之前合併時將第3行2-6列合併,8-11合併,那麼賦值便應該按照列1和列7來賦值,不論是合併操作前還是合併操作後,建議可以先合併,對著Excel看往哪個格子賦值,有時真的想著有點繞。。。

  2. 行之間的合併單元格要先生成才能合併
    我這裡的第二行的第1列和第7列是合併了第三行的,因此需要先進行sheet.createRow(3)操作。

HSSFRow row3 = sheet.createRow(3);
sheet.addMergedRegion(new CellRangeAddress(2, 3, 0, 0));
sheet.addMergedRegion(new CellRangeAddress(2, 3, 6, 6));
row2.createCell(0).setCellValue("單位1");
row2.createCell(6).setCellValue("5");

生成Excel表頭效果大概如下
生成Excel表頭效果

動態生成多頁和表頭

有的專案因資料原因,不會在頁面上生成表格,也不方便生成表格,這時用通用的工具類生成Excel反倒複雜化,而業務需要又是要求動態生成多頁,每頁的表頭還不一樣,雖然不用複雜表頭了,但是寫死的方式更是行不通,這時我們便需要後臺組裝資料,動態拼接頁數和表頭。

@SuppressWarnings("deprecation")
    public static void fromHtmlTable2(List<SimDescDto> dtos, List<Map<String, String>> maps, HttpServletResponse response){

        HSSFWorkbook wb = new HSSFWorkbook();
        HSSFSheet sheet = wb.createSheet("結果");
        HSSFRow row = sheet.createRow(0);
        HSSFRow row2 = sheet.createRow(1);

        HSSFCellStyle cs = wb.createCellStyle();
        //表頭樣式
        HSSFCellStyle thStyle = createCellStyle(wb, true);
        //表體樣式
        HSSFCellStyle tdStyle = createCellStyle(wb, false);
        //第一頁
        HSSFCell c = row.createCell(0);
        c.setCellType(HSSFCell.CELL_TYPE_STRING);
        c.setCellValue("序號");
        c.setCellStyle(thStyle);
        HSSFCell c2 = row2.createCell(0);
        c2.setCellType(HSSFCell.CELL_TYPE_STRING);
        c2.setCellValue(1);
        c2.setCellStyle(tdStyle);
        HSSFCell cell = row.createCell(1);
        cell.setCellType(HSSFCell.CELL_TYPE_STRING);
        cell.setCellValue("身份證號");
        cell.setCellStyle(thStyle);
        HSSFCell cell2 = row2.createCell(1);
        cell2.setCellType(HSSFCell.CELL_TYPE_STRING);
        cell2.setCellValue(dtos.get(0).getSfhm());
        cell2.setCellStyle(tdStyle);
        sheet.setColumnWidth(1, 35*150);    //調整第二列寬度

        for (int k = 0; k < dtos.size(); k++) {
            HSSFSheet sheet2 = wb.createSheet(dtos.get(k).getTablename() + "結果");
            HSSFRow row3 = sheet2.createRow(0);
            HSSFRow row4 = sheet2.createRow(1);
            //後幾頁
            Map<String, String> map = maps.get(k);
            int j = 0;
            if (null != map) {
                for (String key : map.keySet()) {
                    HSSFCell cell3 = row3.createCell(j);
                    cell3.setCellType(HSSFCell.CELL_TYPE_STRING);
                    cell3.setCellValue(key);
                    cell3.setCellStyle(tdStyle);
                    sheet2.setColumnWidth(j, 35*150);
                    HSSFCell cell4 = row4.createCell(j);
                    cell4.setCellType(HSSFCell.CELL_TYPE_STRING);
                    cell4.setCellValue(map.get(key));
                    cell4.setCellStyle(tdStyle);
                    j++;
                }
            }else {
                HSSFCell cell3 = row3.createCell(j);
                cell3.setCellType(HSSFCell.CELL_TYPE_STRING);
                cell3.setCellValue("無");
                cell3.setCellStyle(tdStyle);
            }
        }
        OutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            response.setContentType("application/vnd.ms-excel;utf-8");
            response.addHeader("Content-Disposition", "attachment; filename=\"" + toUtf8("結果")  +   ".xls\"");
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        if(wb != null){
            try {
                wb.write(outputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

動態拼裝實際上和手動拼裝差不多,麻煩的是資料難處理的話就比較繞,比較多頁面的迴圈裡需要巢狀表頭的迴圈,然後又要匹配資料裡的內容,這裡貼的程式碼是處理的較為簡單的兩層迴圈,弄清楚資料迴圈出來和Excel表格對應的地方就行了。

需要注意的地方 

  1. HSSFSheet sheet2 = wb.createSheet()即為建立多個頁面的方法,sheet承載的就是整個頁面的內容,在拼裝時千萬別用混了,還有給值的話便是頁面名,但是千萬不能同名,同名會報錯,Excel匯出時報錯會影響後面內容不會顯示。

  2. create是必不可少的操作,不管是createSheet還是createRow,可是經常會出現頁面內容和行內容為空的情況,這種時候就是因為多次建立了同一頁面和行,即使你沒有使用,只是HSSFRow row3 = sheet2.createRow(0);這樣生成,可是這和Java的new可不一樣,它會預設你重新建立了這一頁或這一行,因此你的內容為空了。所以在迴圈拼裝的時候,create操作放在哪裡一定要仔細,我就是錯了很多次才發現原因的。。。

  3. 有人在使用poi匯出Excel時會有各種編碼問題,包括表名,單元格內容,頁名等等,建議在給單元格賦值時給上cell3.setCellType(HSSFCell.CELL_TYPE_STRING);的處理方式,雖然我測試的時候並沒有什麼影響….然後表名部分是一定要進行utf-8轉換處理的,這裡單獨貼出來通用處理方式。

private static String toUtf8(String s){
        StringBuffer sb = new StringBuffer();
        for (int i=0;i<s.length();i++){
          char c = s.charAt(i);
          if (c >= 0 && c <= 255){sb.append(c);}
          else{
              byte[] b;
              try { b = Character.toString(c).getBytes("utf-8");}
              catch (Exception ex) {
                  System.out.println(ex);
                  b = new byte[0];
              }
              for (int j = 0; j < b.length; j++) {
                  int k = b[j];
                  if (k < 0) k += 256;
                  sb.append("%" + Integer.toHexString(k).toUpperCase());
              }
          }
        }
        return sb.toString();
    }

表名設定時

response.addHeader("Content-Disposition", "attachment; filename=\"" + toUtf8("比對結果")  +   ".xls\"");

相關推薦

專案使用POI匯出Excel一些心得包含複雜表頭動態

最近使用poi做Excel匯出較多,應用場景也各種各樣,導致基本都是按需求自己手動拼的Excel,網上的通用工具類大多不適合業務需要,簡單記錄下來以免下次用到。 通用版(根據jsp上的表格內容動態生成Excel) 工具類 @SuppressWarn

Java web專案利用POI匯出EXCEL表格

SSH2 POI匯出EXCEL表格 1.首先匯入poi的jar包 HSSFWorkbook :工作簿,代表一個excel的整個文件 HSSFSheet:工作表 HSSFRow :行 HSSFCell:單元格 HSSFCellStyle :單元格樣

java 專案利用POI匯出excel總結

在專案中遇到需要把計劃檢修的專案匯出 使用的是Apache POI 對資料進行匯出 1.通過單元格格式來設定表格樣式 對於表格的表頭是固定的 所以先把表頭設定為定值 Workbook workbook = new HSSFWorkbook();

Java使用POI匯出Excel工具類反射

pom.xml: <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId&g

實際專案使用poi匯出excel(spring+springMVC+Mybatis環境下)

1,需要匯出的原始資料.即根據實際需求查詢得到的結果集作為原始資料.可能是一個list,map...看你封裝成什麼樣了(這裡是封裝成list了) 2,將原始的資料轉換到excel中,具體根據資料形式不同,寫法也會不同.不過這些都是大同小異的. 3,將生產好的excel檔案寫到一個路徑下(這裡都是放到t

Javaweb專案,簡單應用Apache POI匯出Excel的簡單例子

直接上程式碼: jsp: 說明:這裡使用ajax請求是會有問題的,會導致瀏覽器視窗不會彈出下載提示和選擇位址列的彈窗 //匯出 $('#btn-export').click(function () { location.href = "${pageContext.r

POIHSSFWorkbook匯出excel封裝的工具類

     在實際中匯出excel非常常見,於是自己封裝了一個匯出資料到excel的工具類,先附上程式碼,最後會寫出例項和解釋。   程式碼中依賴了slf4j日誌包,commons-io包的IOUtils關閉流,commons-lang和commons-collections包等包。 package

在Javaweb利用POI匯出Excel表格

場景:要求利用Excel把報表的資料匯出到本地計算機。 Javaweb生成Excel表格有很多種技術,我選擇了POI技術進行Excel表格的匯入匯出。選擇POI是因為它相容了office2007以上的版本和簡便的操作性。 在此通過簡單的例子說明如何利

Java 使用 Poi 匯出 Excel 通用

上一篇:Java 使用 Poi 匯入 Excel 通用(一) /** * 檔案寫入excel * @param file 檔案 * @param list 資料來源 * @param sheetname 工作簿 * @throws IOException

Java POI 匯出Excel

序言上篇序言已經說明了我為什麼要寫這個教程了,如果想知道請戳這個連結:序言。那麼廢話就不多說開始吧。POI Maven 依賴 <dependencies> <dependency> <groupId>org.apache.poi&

POI匯出Excel

匯出樣式如下: ============================================================== 工具類: package com.util; import java.io.File; import java.io.File

POI匯出Excel

匯出樣式如下: ======================================================================================== 工具類: package com.util; import java.io

springmvc 使用poi匯出excel

看了一些poi匯出excel的部落格 原文地址         http://blog.csdn.net/wangchangpen62/article/details/44410967 匯出excel 工具類的寫法 package com.fuiou.MyProject.

字符、字符集、編碼,以及它們python會遇到的一些問題

區別 做了 and 內部 eve nbsp nes 文字 相對 在看了很多的博客文章之後,總結整理得到了以下文章,非常感謝這些無私奉獻的博主! 文章末尾有本文引用的文章的鏈接,如果有漏掉的文章引用,可以發郵件聯系我,隨後再次附上鏈接! 侵刪!!! 這一部分是下篇,主要

關於spring源碼的一些心得

blog clas 實現接口 繼承 classpath mage 獲取 ssp 源碼 總結:通過前面的一些認識,可以大致認為,ioc容器就是獲取一些需要使用的對象如pojo等的引用,相當於new 而ioc容器的作用也就是用於此處,用於獲取或者讀取對象實例

Linux工作常用到的一些命令持續更新常用的

args 10g $1 conf 運行級別 who 快速 以及 awk 1、查看運行級別3開啟的服務列表: chkconfig --list|grep 3:on 2、查找某類型的文件並計算總大小。 find / -name *.conf -exec w

菜鳥調錯——POI匯出Excel報錯No such file or directory

場景重現 Apache POI Linux Tomcat 如上所示,當時在linux+tomcat的環境下,使用apache的poi匯出excel的時候就會報“No such file or directory”的錯誤。 錯誤資訊 java.la

POI匯出excel表格優化

package com.ywj.excel; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java

HSSFWorkbook(poi)匯出excel表格

本文與另一篇文章關聯: csv格式匯出excel報表 其中: String accountDate 入參(日期) AccountInfoEntityResp accountInfoEntityResp 匯出的xml報文內容(轉換成obj物件) xml報文解析見另一篇:x

專案讀取配置檔案的方式

import java.io.IOException; import java.io.InputStream; import java.util.Properties; /** 有時,需要配置檔案,配置檔案中儲存的內容是什麼 ? 儲存屬性 儲存一些經常