项目中使用POI导出Excel的一些心得(包含复杂表头和动态页数与列数)

来源:互联网 发布:昆特牌 淘宝 编辑:程序博客网 时间:2024/05/18 01:27

最近使用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();            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
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;    } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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>
  • 1
  • 2
  • 3
  • 4
  • 5

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){}%>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

主要就这两个部分,请求方式按自己的来,表名需要转化编码,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));
  • 1
  • 2
  • 3
  • 4
  • 5

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  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");
  • 1
  • 2
  • 3
  • 4
  • 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();            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

动态拼装实际上和手动拼装差不多,麻烦的是数据难处理的话就比较绕,比较多页面的循环里需要嵌套表头的循环,然后又要匹配数据里的内容,这里贴的代码是处理的较为简单的两层循环,弄清楚数据循环出来和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();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

表名设置时

response.addHeader("Content-Disposition", "attachment; filename=\"" + toUtf8("比对结果")  +   ".xls\"");
阅读全文
0 0
原创粉丝点击