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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM