java封装导出excel之——优化

      上次,我们说道了如何使用HSSFWorkbook(POI)实现导出excel的功能。而且我们也已经实现了。但是,我们发现,当我们导出几万设置几十万的数据时,就会很慢,而且还会造成内存溢出的问题。如果经常下载大量数据,还会占用服务器上的存储空间。今天,我们来优化一下我们的导出excel。

1、生成多个excel文件

      当数据量比较大时,我们选择让每个excel分为多个sheet,并且生成多个excel,然后将多个excel压缩,最后输出。

/**
     * 导出Excel
     * @param excelName   要导出的excel名称
     * @param list   要导出的数据集合
     * @param fieldMap 中英文字段对应Map,即要导出的excel表头
     * @param response  使用response可以导出到浏览器
     * @return
     */ 
    public static <T> void export(String excelName,List<T> list,LinkedHashMap<String, String> fieldMap,HttpServletResponse response){
        // 设置默认文件名为当前时间:年月日时分秒
        if (excelName==null || excelName=="") {
            excelName = new SimpleDateFormat("yyyyMMddhhmmss").format(
                    new Date()).toString();
        }

        int total=list.size();  //总记录数
        int  excelBig=10000;    //excel划分的大小

        //判断需要分成多少个excel
        int excelCount=(total%excelBig==0)?(total/excelBig):(total/excelBig+1);

        //定义一个excel集合
        List<HSSFWorkbook>  excelList=new ArrayList<HSSFWorkbook>();

        HSSFWorkbook wb=null;  //excel工作表
        int begin = 0;   //数据源的开始位置
        int end = 0;      //数据源的结束位置

        //循环得到excel集合
        for(int i=0;i<excelCount;i++){
            //第一次begin从零开始,否则从结束位置加1开始
            if(end!=0){
                begin=end;
            }
            end=begin+excelBig;
            //判断如果最后的位置大于总记录数,则修改为总记录数
            if(end>list.size()){
                end=list.size();
            }
            //生成excel
            wb=createExcel(excelName+(i+1), list,begin,end, fieldMap, response);
            //将生成的excel添加到集合
            excelList.add(wb);
        }

        //生成压缩文件,并导出
        toZipOut(excelName, excelList, response);
    }

2、每个excel生成多个sheet

/**
     * 生成Excel
     * @param excelName   要导出的excel名称
     * @param list       要导出的数据集合
     * @param begin   数据集合的开始位置
     * @param end      数据集合的结束位置
     * @param fieldMap 中英文字段对应Map,即要导出的excel表头
     * @param response  使用response可以导出到浏览器
     * @return
     */ 
    private static <T> HSSFWorkbook createExcel(String excelName,List<T> list,Integer begin,Integer end,
            LinkedHashMap<String, String> fieldMap,HttpServletResponse response){

        int sheetContent=2000;     //每个excel中sheet的容量

        //计算sheet的数量
        int sheetCount=0;
        //如果总记录数  正好整除 以每个sheet的容量
        if ((end-begin)%sheetContent==0){
            //sheet的数量=总记录数 除以 每个sheet的容量
            sheetCount=(end-begin)/sheetContent;
        }else{
            //sheet的数量=总记录数 除以 每个sheet的容量 加1
            sheetCount=(end-begin)/sheetContent+1;
        }

        //创建一个WorkBook,对应一个Excel文件
        HSSFWorkbook wb=new HSSFWorkbook();
        //创建单元格,并设置值表头 设置表头居中
        HSSFCellStyle style=wb.createCellStyle();
        //创建一个居中格式
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);

        //定义sheet
        HSSFSheet sheet=null;
        //定义sheet的名称
        String sheetName=excelName+"-";
        //sheet中的数据开始位置
        int beginSheet=0;
        //sheet中的数据结束位置
        int endSheet=0;

        //循环创建sheet
        for (int i=0;i<sheetCount;i++){
            //在Workbook中,创建一个sheet,对应Excel中的工作薄(sheet)
            sheet=wb.createSheet(sheetName+(i+1));

            //第一次beginSheet从begin开始,否则从结束位置加1开始
            if(endSheet==0){
                beginSheet=begin;
            }else{
                beginSheet=endSheet;
            }
            //sheet结束位置=sheet开始位置+每个sheet的容量
            endSheet=beginSheet+sheetContent;
            //判断如果最后的位置大于总记录数,则修改为总记录数
            if(endSheet>end){
                endSheet=end;
            }

            // 填充工作表
            try {
                fillSheet(sheet,list,beginSheet,endSheet,fieldMap,style);
            } catch (Exception e) {
                logger.info(e.getMessage());
            }
        }
        //返回excel
        return wb;
    }

3、将文件压缩为zip格式

/**
     * 生成压缩文件,并导出
     * @param excelName   要导出的excel名称
     * @param excelList   要压缩的文件集合
     * @param response  使用response可以导出到浏览器
     * @return
     */     
    private static <T> void toZipOut(String excelName,List<HSSFWorkbook> excelList,HttpServletResponse response){

        try {
            //将压缩包的名字默认为日期时间串
            String fieldName=new SimpleDateFormat("yyyyMMddhhmmss").format(
                    new Date()).toString();

            /**设置response头信息**/
            //重置response头信息
            response.reset();  
            //自己写状态码
            response.setStatus(HttpServletResponse.SC_OK);  
            //使客户端浏览器,区分不同种类的数据,并根据不同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据。
            //ZIP与EXE文件的MIME类型同为application/octet-stream
            response.setContentType( "application/octet-stream "); 
            //Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。
            //Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。 
            //此处设置下载文件的格式为.zip
            response.setHeader("Content-Disposition","attachment;filename=\""+fieldName+".zip"+"\"");  

            //生成输出流
            OutputStream ouputStream = response.getOutputStream();
            //生成压缩包
            ZipOutputStream zip = new ZipOutputStream(ouputStream);
            zip.setEncoding("GBK");//指定编码为gbk,否则部署到linux下会出现乱码
            int i = 1;
            String eName;
            //循环文件列表,添加到压缩包中
            for(HSSFWorkbook workbook:excelList){
                //给每个文件名加序号
                eName=excelName+String.valueOf(i);
                //实例化一个压缩实体
                ZipEntry entry = new ZipEntry(eName+".xls");
                //将压缩实体放入压缩包
                zip.putNextEntry(entry);
                //将excel内容写进压缩实体
                workbook.write(zip);
                i++;
            }
            //将文件输出
            zip.flush();
            zip.close();
            response.flushBuffer();  
        } catch (Exception e) {
            logger.info("导出Excel失败!");
            logger.error(e.getMessage());
        }
    }

总结:

      这里我们只是将大数据进行了切割,转而变成处理小数据,这只是做了一部分的优化处理,希望大家能有更好的优化方法。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值