zoukankan      html  css  js  c++  java
  • java 导出Excel 大数据量,自己经验总结!

    出处: http://lyjilu.iteye.com/

    分析导出实现代码,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

  • 相关阅读:
    【bzoj3211】花神游历各国
    四维偏序(K-D-Tree+rebuild)
    【bzoj3295】动态逆序对
    BZOJ3444: 最后的晚餐
    BZOJ3091: 城市旅行
    BestCoder Round #72
    BZOJ2733: [HNOI2012]永无乡
    wc2016总结
    BZOJ4381: [POI2015]Odwiedziny
    BZOJ4196: [Noi2015]软件包管理器
  • 原文地址:https://www.cnblogs.com/onlymate/p/4828428.html
Copyright © 2011-2022 走看看