分析导出实现代码,XLSX支持:
/** * 生成<span style="white-space: normal; background-color: #ffffff;">XLSX</span>,2007版本的excel,每个sheet无6.5W的行数限制,但是到了一定数量,可能内存溢出, * 次方法适合在预计10W以下的数据导出时使用,本机测试,14W可以导出。列数量在8列左右 * * @param fileOut * 输出流 * @param sheetMap * 要设置的数据信息 * @throws SQLException */ public static void createXSLXByResultSet(OutputStream fileOut, WriteXLSBean... beans) throws SQLException { try { //重点 Workbook Workbook wb = new XSSFWorkbook(); for (int i = 0, len = beans.length; i < len; i++) { WriteXLSBean xlsBean = beans[i]; Sheet sheet = wb.createSheet(xlsBean.getSheetName()); ResultSet rs = xlsBean.getRs(); ResultSetMetaData rsmd = rs.getMetaData(); TypeHandlerRegistry tr = BeanContext.tr; Map<String, String> th = xlsBean.getTh(); int index = 0; while (rs.next()) { long t1 = System.currentTimeMillis(); org.apache.poi.ss.usermodel.Row row = sheet .createRow(index); for (int j = 0, numberOfColumns = rsmd.getColumnCount(); j < numberOfColumns; j++) { String key = rsmd.getColumnLabel(j + 1).toLowerCase(); if (th.containsKey(key)) { TypeHandler<?> type = tr.getTypeHandler(JdbcType .forCode(rsmd.getColumnType(j + 1))); Object obj = type.getResult(rs, key); row.createCell(j).setCellValue(obj == null ? "" : obj.toString()); } } System.out.println(index + " :" + (System.currentTimeMillis() - t1)); index++; } } //重点 Workbook wb.write(fileOut); } catch (IOException e) { e.printStackTrace(); throw new ServiceRunTimeException("生产xls文档错误", e); } finally { } }
在上面 标注了重点的两处,分别是:
1.构建一个Excel对象
2.将该对象写入一个OutPutStream
而在构建过程中,没有地方写入OutPutSteam ,也就是说必须在内存中构建整个 Excel,才能进行写出操作,在大数据量情况下,这样将导致所有数据加载到内存中,而不能输出,导致最后 内存溢出。
根据运行环境不用,可能内存溢出的 情况不同
根据情况,如果数据量达到10W以上,建议使用
1、多个Excel,每个Excel一个Sheet,因为所有Sheet都是Workbook的组成部分。如果不分多个Excel,即使分Sheet也没用,
2、每个Excel中列数适中,比如: 5W行每个Excel档,实现分多次导出和分页查询原理一样
3、对多个Excel导出到一个临时目录,并通过程序压缩,然后提供给客户下载
2003版通过数据库结果存到List中,然后进行生产:Table 就是List Row 是Map
/** * 生产xls,2003版本的excel,每个sheet有6.5W的行数限制 * * @param fileOut * 输出流,未关闭 * @param sheetMap * 要导出的数据信息 */ public static void createXSLByMap(OutputStream fileOut, Map<String, Table<Row<String, Object>>> sheetMap) { try { HSSFWorkbook wb = new HSSFWorkbook(); Set<String> keys = sheetMap.keySet(); for (Iterator<String> iterator = keys.iterator(); iterator .hasNext();) { String SheetKey = iterator.next(); Sheet sheet = wb.createSheet(SheetKey); List<Row<String, Object>> sheetRows = sheetMap.get(SheetKey); for (int i = 0, len = sheetRows.size(); i < len; i++) { Map<String, Object> cellMap = sheetRows.get(i); Set<String> cellSet = cellMap.keySet(); org.apache.poi.ss.usermodel.Row row = sheet.createRow(i); int j = 0; for (Iterator<String> iterCell = cellSet.iterator(); iterCell .hasNext(); j++) { String cellKey = iterCell.next(); Object obj = cellMap.get(cellKey); row.createCell(j).setCellValue(obj == null ? "" : obj.toString()); } } } wb.write(fileOut); } catch (IOException e) { e.printStackTrace(); throw new ServiceRunTimeException("生产xls文档错误", e); } finally { } }
新版本 POI+office 2007版本excel可以导出几十万条而不内存溢出,详细见:
导出大量数据到 excel 的 xlsx文件
static String src="abcdefafslfelgtryjukjhgfdadertjDSFGHJKJGHFERTUIOabcdefafslfelgtryjukjhgfdadertjDSFGHJKdertjDSFGHJKJGHFERTUIOabcdefafslfelgtryjukjhgfdadertjDSFGHJKJGHFERTUIO"; public static void main(String[] args) throws Throwable { SXSSFWorkbook wb = new SXSSFWorkbook(100); // 这里100是在内存中的数量,如果大于此数量时,会写到硬盘,以避免在内存导致内存溢出 Sheet sh = wb.createSheet(); for (int rownum = 0; rownum < 1000000; rownum++) { Row row = sh.createRow(rownum); for (int cellnum = 0; cellnum < 10; cellnum++) { Cell cell = row.createCell(cellnum); String address = new CellReference(cell).formatAsString(); cell.setCellValue(address+src.substring(rownum%10*10+1, (rownum%10+1)*10)); } } File file = new File("F:/aa.xlsx"); file.createNewFile(); FileOutputStream out = new FileOutputStream(file); wb.write(out); out.close(); }
内存使用情况:
根据以上前辈经验,自己在结果自身需求考虑,整合出一个工具。解决了excle表.xls格式行数65535行的限制。我实现的形式是导出一张表,里面有多页(我是已65000为一页)
这里是用反射来使用所有传入进行的实体的属性的值。这里只针对String和基本数据类型。如有自己定义的类型需要自己加上。
package com.tommy.fundation.util; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Date; import java.util.List; public class RelectUtil { public static <T> List<Object> reflectEntity(T model,Class<?> cals) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchFieldException{ List<Object> list = new ArrayList<Object>(); Field[] field = model.getClass().getDeclaredFields(); //获取实体类的所有属性,返回Field数组 for(int j=0 ; j<field.length ; j++){ //遍历所有属性 String nam = field[j].getName(); //获取属性的名字 String name = nam; name = name.substring(0,1).toUpperCase()+name.substring(1); String type = field[j].getGenericType().toString(); //获取属性的类型 if(type.equals("class java.lang.String")){ //如果type是类类型,则前面包含"class ",后面跟类名 Method m = model.getClass().getMethod("get"+name); String value = (String) m.invoke(model); //调用getter方法获取属性值 if(value != null){ list.add(value); }else{ list.add(""); } } if(type.equals("class java.lang.Integer")){ Method m = model.getClass().getMethod("get"+name); Integer value = (Integer) m.invoke(model); if(value != null){ list.add(value); }else{ list.add(""); } } if(type.equals("class java.lang.Short")){ Method m = model.getClass().getMethod("get"+name); Short value = (Short) m.invoke(model); if(value != null){ list.add(value); }else{ list.add(""); } } if(type.equals("class java.lang.Double")){ Method m = model.getClass().getMethod("get"+name); Double value = (Double) m.invoke(model); if(value != null){ list.add(value); }else{ list.add(""); } } if(type.equals("class java.lang.Boolean")){ Method m = model.getClass().getMethod("get"+name); Boolean value = (Boolean) m.invoke(model); if(value != null){ list.add(value); }else{ list.add(""); } } if(type.equals("class java.util.Date")){ Method m = model.getClass().getMethod("get"+name); Date value = (Date) m.invoke(model); if(value != null){ list.add(value); }else{ list.add(""); } } } return list; } }
下面将是重点实现导出excel表
package com.tommy.fundation.util; import java.io.OutputStream; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletResponse; import org.apache.poi.hssf.record.formula.functions.T; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; @SuppressWarnings("hiding") public class ExportExcel<T> { /** * 导出多张excel表,解决xls格式行数65535的限制 * @author OnlyOne * @param response * @param list 需要处理的list数据集合 * @throws Exception */ @SuppressWarnings("deprecation") public void doExcel(HttpServletResponse response,List<T> list,String fileName) throws Exception { OutputStream os = response.getOutputStream();//获取输出流 response.reset(); // 设置下载头部信息。Content-disposition为属性名。attachment表示以附件方式下载,如果要在页面中打开,则改为inline。filename为文件名 response.setHeader("Content-disposition", "attachment; filename=excell.xls"); response.setContentType("application/msexcel"); Map<Integer, List<T>> sheetMap = daData(list); HSSFWorkbook wb = new HSSFWorkbook(); Set<Integer> keys = sheetMap.keySet(); for (Iterator<Integer> iterator = keys.iterator(); iterator.hasNext();) { Integer SheetKey = iterator.next(); HSSFSheet sheet = wb.createSheet((fileName+SheetKey).toString()); List<T> sheetRows = sheetMap.get(SheetKey); for (int i = 0, len = sheetRows.size(); i < len; i++) { T en = (T) sheetRows.get(i); List<Object> dataList = RelectUtil.<T>reflectEntity(en, en.getClass()); HSSFRow row = sheet.createRow(i); row.createCell(0).setCellValue(String.valueOf(i)); for(int m=0; m<dataList.size(); m++){ row.createCell(m+1).setCellValue(dataList.get(m).toString()); } } } wb.write(os); } /** * 此方法将数据集合按65000个进行分割成多个子集合 * @author OnlyOne * @param list 需要处理的list数据集合 * @return */ public Map<Integer, List<T>> daData(List<T> list){ int count = list.size()/65000; int yu = list.size() % 65000; Map<Integer, List<T>> map = new HashMap<Integer, List<T>>(); for (int i = 0; i <= count; i++) { List<T> subList = new ArrayList<T>(); if (i == count) { subList = list.subList(i * 65000, 65000 * i + yu); } else { subList = list.subList(i * 65000, 65000 * (i + 1)-1); } map.put(i, subList); } return map; } }
在Java中调用的方式
@RequestMapping(value = "/doExcel", method = RequestMethod.GET) public void doExcel(HttpServletResponse response,HttpServletRequest request) throws Exception { List<Enrolltg> list = enrolltgService.findAll(); new ExportExcel<Enrolltg>().doExcel(response, list, "黑白淡奶"); }
大功搞成,以后再也不会为了数据量太大而导不出来烦恼了!!!
需要的包 poi-3.2-FINAL-20081019.jar