struts2+poi 导出Excel文件
1.首先贴出对应的struts配置文件
1 <action name="exportExcel" class="com.action.common.ExcelAction"> 2 <result name="success" type="stream"> 3 <param name="contentType">application/vnd.ms-excel;charset=utf-8</param> 4 <param name="inputName">excelStream</param> 5 <param name="contentDisposition">attachment;filename="${fileName}.xls"</param> 6 <param name="bufferSize">1024</param> 7 </result> 8 <result name="error">../report/DataEmpty.jsp</result> 9 </action>
其中,stream类型的结果共可以指定7个参数:
- contentType:下载文件的类型。
- contentLength:下载文件的长度,用于浏览器的进度条显示。
- contentDisposition:指定文件下载的默认名字,如果不指定则使用Action名.action。
- inputName:Action中用于返回InputStream的get方法的名字,默认为inputStream,因此,我们的Action中定义了getInputStream的方法。
- bufferSize:缓冲区大小,默认为(1024)即1k。
- allowCaching:是否允许浏览器进行缓存。
- contentCharSet:HTTP响应头信息中的编码方式。
2.在对应的Action中定义输出流和文件名
2.1 InputStream excelStream; //这个输入流对应上面struts.xml中配置的那个excelStream,两者必须一致
String fileName; //这个名称就是用来传给上面struts.xml中的${fileName}的
并生成get、set方法
然后得到jsp页面传过来的值,可以多个jsp页面的多个按钮使用着同一个Action,只要传过来的值其中有一个作为识别条件即可。如1.jap和2.jsp都要调用此方法,那1.jsp传过来一个“A”,2.jsp传过来一个“B”,根绝A或者B的不同调用不同的List生成方法,就可以实现。
是的,这里下一步就是生成一个存有预期输出结果的List,生成方法可考虑单独建立方法文件,当需要实现多个页面调用或者一个页面多出调用Action方法的时候,建议List生成方法分别些方法文件或几种一个文件,尽量避免放在Action中。
下面的步骤基本如下
1 if(null == list ){ 2 3 this.setVestige(actionHistory+"失败!"); 4 5 return "error"; 6 7 }else{ 8 9 //创建Excel工作簿方法2.2 10 11 HSSFWorkbook workbook = getWorkbook(list,sheetName,isSum); 12 13 if (null != workbook) { 14 15 try { 16 17 //将工作簿写入最上面定义的InputStream流——名称为excelStream,这个名字对应struts.xml中配置的inputName参数 18 19 this.workbook2InputStream(workbook,fileName); 20 21 return "success"; 22 23 }catch (IOException e) { 24 25 e.printStackTrace(); 26 27 return "error"; } 28 29 }else{ 30 31 return "error"; 32 33 } 34 35 }
2.2 接下来就是将List转化成Excel工作表的方法。
方法中除了实际保存数据的list外还有一个以“,”号分割的字符串,此字符串用来保存sheet名
考虑到有可能需要做统计因此,使用一个Int值来记录是否需要统计,需要做统计的话,List信息中最好也将统计好的结果保存,这样只需要在最后一行时合并单元格等处理后直接输出结果
1 private HSSFWorkbook getWorkbook(List listM,String sheetNames,int isSum) throws Exception { 2 HSSFWorkbook workbook = new HSSFWorkbook();//创建Excel文件 3 4 HSSFCellStyle style = workbook.createCellStyle(); // 样式对象 5 style.setAlignment(HSSFCellStyle.ALIGN_CENTER); 6 style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);// 指定单元格垂直居中对齐 7 //////////////////// 8 String[] sheetName = sheetNames.split(","); 9 for(int k = 0; k < listM.size(); k++){ 10 List list = (List) listM.get(k); 11 HSSFSheet sheet = workbook.createSheet();//创建sheet 12 workbook.setSheetName(k, sheetName[k], HSSFCell.ENCODING_UTF_16);//为指定第K个sheet命名,并设定字符集类型 13 14 String[] columnNames = (String[]) list.get(0); 15 16 HSSFRow row = sheet.createRow(0); // 创建第1行,也就是输出表头 17 HSSFCell cell = row.createCell((short) 0); 18 cell.setCellType(HSSFCell.CELL_TYPE_STRING); 19 cell.setEncoding(HSSFCell.ENCODING_UTF_16); 20 for (short i = 0; i < columnNames.length; i++) { 21 cell = row.createCell(i); // 创建第i列 22 cell.setCellType(HSSFCell.CELL_TYPE_STRING); 23 cell.setEncoding(HSSFCell.ENCODING_UTF_16); 24 cell.setCellValue(columnNames[i]); 25 } 26 27 List colunData = null; 28 // 下面是输出各行的数据(留出最后一行,如果是合计信息单独输出,如果非合计信息按照原本样式输出) 29 if(list.size() > 1){ 30 for(int i = 1 ;i < list.size()-1;i++){ 31 colunData = (List) list.get(i); 32 row = sheet.createRow(i);// 创建第i行 33 for (short j = 0; j < colunData.size(); j++) { 34 cell = row.createCell(j); // 创建第j列 35 cell.setCellType(HSSFCell.CELL_TYPE_STRING); 36 cell.setEncoding(HSSFCell.ENCODING_UTF_16); 37 cell.setCellValue(colunData.get(j).toString()); 38 } 39 } 40 if(isSum == 0){//无需统计信息 41 colunData = (List) list.get(list.size()-1); 42 row = sheet.createRow(list.size()-1);// 创建第i行 43 for (short j = 0; j < colunData.size(); j++) { 44 cell = row.createCell(j); // 创建第j列 45 cell.setCellType(HSSFCell.CELL_TYPE_STRING); 46 cell.setEncoding(HSSFCell.ENCODING_UTF_16); 47 cell.setCellValue(colunData.get(j).toString()); 48 } 49 }else if(isSum == 1 ){ 50 colunData = (List) list.get(list.size()-1);//此处留下最后一列 51 row = sheet.createRow(list.size()-1);// 创建第i行 52 sheet.addMergedRegion(new Region(list.size()-1, (short)0, list.size()-1, (short) 2)); 53 for (short j = 0; j < colunData.size(); j++) { 54 if(j == 0){ 55 cell = row.createCell(j); // 创建第j列 56 cell.setCellStyle(style);//设置居中 57 }else{ 58 cell = row.createCell((short) (j+2)); // 创建第j列 59 } 60 cell.setCellType(HSSFCell.CELL_TYPE_STRING); 61 cell.setEncoding(HSSFCell.ENCODING_UTF_16); 62 cell.setCellValue(colunData.get(j).toString()); 63 } 64 } 65 } 66 } 67 return workbook; 68 }
2.3 如果上面方法返回得到的 workbook不为null的话就可以进行下面的方法了
1 /** 2 * 将Workbook写入到InputStream 3 * 4 * @param workbook HSSFWorkbook 5 * @param fileName String 文件名 6 * 7 * */ 8 private void workbook2InputStream(HSSFWorkbook workbook, String fileName) throws Exception { 9 this.setFileName(URLEncoder.encode(fileName,"UTF-8")); 10 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 11 workbook.write(baos); 12 baos.flush(); 13 byte[] aa = baos.toByteArray(); 14 excelStream = new ByteArrayInputStream(aa, 0, aa.length) 15 baos.close(); 16 }
3、其他
至此,大概你是用IE的浏览器已经能完全正常的运行,并使用了。可是使用谷歌或者火狐的浏览器就会发现生成的文件名里面如果有中文的话就会乱码,相信这不会是你想要的结果,因此如果要跨浏览器,你需要多一步。设置返回文件名的编码
1 public void setFileName(String fileName) throws UnsupportedEncodingException{ 2 HttpServletRequest request = ServletActionContext.getRequest(); 3 String agent = request.getHeader("User-Agent"); 4 if(null != agent){ 5 agent = agent.toLowerCase(); 6 if (agent.indexOf("firefox") != -1) { 7 this.fileName = new String(URLDecoder.decode(fileName,"UTF-8").getBytes(),"iso-8859-1"); 8 }else { 9 this.fileName = fileName; 10 } 11 } 12 }
以上方法就可以实现对多浏览器的支持了
存在问题:
1.Excel文件行数为65536行,超过之后需要手动设置分页,
2.执行过程中需要手动设置jvm虚拟内存,否则会内存溢出。
错误代码为
java.lang.OutOfMemoryError: Java heap space
java.util.Arrays.copyOf(Arrays.java:2786)
java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94)
java.io.OutputStream.write(OutputStream.java:58)
解决方法是:修改Intalled JREs配置
window->Preferences->Java->Installed JREs,选择当前的JRE,然后edit它;在新窗口里设置Default VM Arguments为 -Xms256M -Xmx512M即可;重新打包通过;
如果另有其他问题,欢迎来信,共同探讨。
部分方法借鉴自网上,出处因时间久难以查找,请原作者见谅。