java批量文件打包成压缩成zip下载和大量数据导出excel时的处理方法

对于我们来说,java导出数据成excel或其他数据文件,或者下载资源是开发中的家常便饭, 但是在导出的时候,如果点击一个按钮导出几百万条数据,如果不作处理的话很可能会出现一系列的问题. 这里介绍打包成zip压缩包下载

针对大量数据导出excel, 这里有几种办法:
1. 每到一定数量就分成一个sheet
2. 每到一定数量分成一个excel,压缩成zip包打包下载
3. 控制导出的数据量,或者分页查询
4. 加缓存,用POI官方的api
SXSSFWorkbook workbook = new SXSSFWorkbook(rowCache);

我这边用的是2和4 , 整体的思路就是,如果是小数目的数据,直接下载excel,如果是大量数据,先转换成一个个excle放在一个临时文件夹中, 然后打包临时文件夹成zip包返回给浏览器,然后删除临时文件夹

[下载文件的工具类]

先准备好自己定义的工具类,之后的方法里面都需要用到这个工具类, 包括设置两头一流, 压缩文件,删除文件夹等


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Encoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@SuppressWarnings("restriction")
public class FileDownloadUtils {

    private static final Logger logger = LoggerFactory.getLogger(FileDownloadUtils.class);

    /**
     * 编译下载的文件名
     * @param filename
     * @param agent
     * @return
     * @throws IOException
     */
    public static String encodeDownloadFilename(String filename, String agent)throws IOException {
        if (agent.contains("Firefox")) { // 火狐浏览器
            filename = "=?UTF-8?B?"
                    + new BASE64Encoder().encode(filename.getBytes("utf-8"))
                    + "?=";
            filename = filename.replaceAll("\r\n", "");
        } else { // IE及其他浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+"," ");
        }
        return filename;
    }

    /**
     * 创建文件夹;
     * @param path
     */
    public static void createFile(String path) {
        File file = new File(path);
        //判断文件是否存在;
        if (!file.exists()) {
            //创建文件;
            file.mkdirs();
        }
    }

    /**
     * 生成.zip文件;
     * @param path
     * @throws IOException
     */
    public static ZipOutputStream craeteZipPath(String path) throws IOException{
        ZipOutputStream zipOutputStream = null;
        File file = new File(path+DateUtils.getDateWx()+".zip");
        zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
        File[] files = new File(path).listFiles();
        FileInputStream fileInputStream = null;
        byte[] buf = new byte[1024];
        int len = 0;
        if(files!=null && files.length > 0){
            for(File excelFile:files){
                String fileName = excelFile.getName();
                fileInputStream = new FileInputStream(excelFile);
                //放入压缩zip包中;
                zipOutputStream.putNextEntry(new ZipEntry(path + "/"+fileName));
                //读取文件;
                while((len=fileInputStream.read(buf)) >0){
                    zipOutputStream.write(buf, 0, len);
                }
                //关闭;
                zipOutputStream.closeEntry();
                if(fileInputStream != null){
                    fileInputStream.close();
                }
            }
        }

        /*if(zipOutputStream !=null){
            zipOutputStream.close();
        }*/
        return zipOutputStream;
    }


    /**
     * //压缩文件
     * @param srcfile   要压缩的文件数组
     * @param zipfile  生成的zip文件对象
     */
    public static void ZipFiles(java.io.File[] srcfile, File zipfile) throws Exception {
        byte[] buf = new byte[1024];
        FileOutputStream fos = new FileOutputStream(zipfile);
        ZipOutputStream out = new ZipOutputStream(fos);
        for (int i = 0; i < srcfile.length; i++) {
            FileInputStream in = new FileInputStream(srcfile[i]);
            out.putNextEntry(new ZipEntry(srcfile[i].getName()));
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            out.closeEntry();
            in.close();
        }
        out.close();
        fos.flush();
        fos.close();
    }

    /**
     * 删除文件夹及文件夹下所有文件
     * @param dir
     * @return
     */
    public static boolean deleteDir(File dir) {
        if (dir == null || !dir.exists()){
            return true;
        }
        if (dir.isDirectory()) {
            String[] children = dir.list();
            //递归删除目录中的子目录下
            for (int i=0; i<children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空,可以删除
        return dir.delete();
    }

    /**
     * 生成html
     * @param msg
     * @return
     * @author zgd
     * @time 2018年6月25日11:47:07
     */
    public static String getErrorHtml(String msg) {
        StringBuffer sb = new StringBuffer();
        sb.append("<html>");
        sb.append("<head>");
        sb.append("<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>");
        sb.append("</head>");
        sb.append("<body>");
        sb.append("<div id='errorInfo'> ");
        sb.append("</div>");
        sb.append("<script>alert('"+msg+"')</script>");
        sb.append("</body>");
        sb.append("</html>");
        return sb.toString();
    }


    /**
     * 设置下载excel的响应头信息
     * @param response
     * @param request
     * @param agent
     * @param fileName
     * @throws IOException
     * @author zgd
     * @time 2018年6月25日11:47:07
     */
    public static void setExcelHeadInfo(HttpServletResponse response, HttpServletRequest request, String fileName)  {
        try {
            // 获取客户端浏览器的类型
            String agent = request.getHeader("User-Agent");
            // 对文件名重新编码
            String encodingFileName = FileDownloadUtils.encodeDownloadFilename(fileName, agent);
            // 告诉客户端允许断点续传多线程连接下载
            response.setHeader("Accept-Ranges", "bytes");
            //文件后缀
            response.setContentType("application/vnd.ms-excel;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + encodingFileName);
        } catch (IOException e) {
            logger.error(Thread.currentThread().getStackTrace()[1].getMethodName() +"发生的异常是: ",e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置下载zip的响应头信息
     * @param response
     * @param fileName 文件名
     * @param request
     * @throws IOException
     * @author zgd
     * @time 2018年6月25日11:47:07
     */
    public static void setZipDownLoadHeadInfo(HttpServletResponse response, HttpServletRequest request, String fileName) throws IOException {
        // 获取客户端浏览器的类型
        String agent = request.getHeader("User-Agent");
        response.setContentType("application/octet-stream ");
        // 表示不能用浏览器直接打开
        response.setHeader("Connection", "close");
        // 告诉客户端允许断点续传多线程连接下载
        response.setHeader("Accept-Ranges", "bytes");
        // 对文件名重新编码
        String encodingFileName = FileDownloadUtils.encodeDownloadFilename(fileName, agent);
        response.setHeader("Content-Disposition", "attachment; filename=" + encodingFileName);
    }

}

1.控制层接收参数

/**
     * 导出商家订单数据列表的excel文件
     *
     * @param request
     * @author zgd
     * @time 2018年6月5日14:47:21
     */
    @RequestMapping(value = "/exportStoreOrderList")
    public void exportStoreOrderList(HttpServletRequest request, HttpServletResponse response) {
       /*
       * 从service层获取List的数据,此处省略
       */
        List<Map<String, Object>> list = data;

        //导出excel
        // 创建Excel文件,每个excel限制10000条数据,超过则打包zip
        int size = 10000;
        //每次缓存1000条到内存,其余写到磁盘
        int rowCache = 1000;
        String fileName = "商家订单数据-" + DateUtils.getDateWx();
        // 创建Excel文件,每个excel限制10000条数据,超过则打包zip
        int size = 10000;
        //每次缓存1000条到内存,其余写到磁盘
        int rowCache = 1000;
        String fileName = "商家订单数据-" + DateUtils.getDateWx();
        //导出excel
        try {
            //excel文件个数
            int n = list.size() / size + 1;
            if (list != null) {
                SXSSFWorkbook workbook = getStoreOrderExcel(list, rowCache);
                if (n == 1) {
                    //下载单个excle
                    fileName = fileName + ".xls";
                    FileDownloadUtils.downloadExcel(request, response, fileName, workbook);
                } else {
                    fileName = "批量" + fileName + ".zip";
                    String realPath = request.getSession().getServletContext().getRealPath("WEB-INF");
                    //创建临时文件夹保存excel
                    String tempDir = realPath + "/tempDir/" + DateUtils.getDateWx();
                    List<File> files = getStoreOrderExcels(tempDir, size, list, rowCache);
                    String zipPath = tempDir + "\\" + fileName;
                    //下载zip
                    FileDownloadUtils.downloadZip(request, response, fileName, files, zipPath);
                    //删除tempDir文件夹和其中的excel和zip文件
                    boolean b = FileDownloadUtils.deleteDir(new File(realPath + "\\tempDir"));
                    if (!b) {
                        throw new RuntimeException("tempDir文件夹及其中的临时Excel和zip文件删除失败");
                    }
                }
            }
        }catch (Exception e) {
            try {
                if (!response.isCommitted()) {
                    response.setContentType("text/html;charset=utf-8");
                    response.setHeader("Content-Disposition", "");
                    String html = FileDownloadUtils.getErrorHtml("下载失败");
                    response.getOutputStream().write(html.getBytes("UTF-8"));
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

2. getStoreOrderExcel方法解析

 /**
     * 将数据库查出来的商家订单数据,创建成excel
     * @param list  商家订单数据
     * @param rowCache  缓冲的行数
     * @return
     */
    private SXSSFWorkbook getStoreOrderExcel(List<Map<String, Object>> list, int rowCache) {
        //如果rowCache是1000,就是每次读取1000条数据到缓存中
         SXSSFWorkbook workbook = new SXSSFWorkbook(rowCache);
         /**
            ....设置行,列,塞入数据
         **/
         return workbook;
    }

2.1 这里分成两条线: 下载excel和下载zip

2.2 下载excel支线

2.2.1 downloadExcel方法解析
/**
     * 下载excel
     *
     * @param request
     * @param response
     * @param fileName
     * @param workbook
     * @throws Exception
     * @author zgd
     * @time 2018年6月25日11:47:07
     */
    private void downloadExcel(HttpServletRequest request, HttpServletResponse response, String fileName, SXSSFWorkbook workbook) {
        //一个流两个头
        //设置下载excel的头信息
        FileDownloadUtils.setExcelHeadInfo(response, request, fileName);

        // 写出文件
        ServletOutputStream os = null;
        try {
            os = response.getOutputStream();
            workbook.write(os);
        } catch (IOException e) {
            logger.error(Thread.currentThread().getStackTrace()[1].getMethodName() + "发生的异常是: ", e);
            throw new RuntimeException(e);
        } finally {
            try {
                if (os != null) {
                    os.flush();
                    os.close();
                }
                if (workbook != null) {
                    workbook.close();
                }
            } catch (Exception e1) {
                logger.error(Thread.currentThread().getStackTrace()[1].getMethodName() + "发生的异常是: ", e1);
                throw new RuntimeException(e1);
            }
        }
    }

下载excel支线完毕

2.3 下载zip支线

2.3.1 getStoreOrderExcels方法解析,先将大量数据转化成批量的excel临时文件
 /**
     * 将数据转成多个excel文件放在项目中
     *
     * @param tempDir
     * @param size     每个excel的数据的行数
     * @param list     数据
     * @param rowCache 下载时缓存的行数
     * @throws Exception
     * @author zgd
     * @time 2018年6月25日11:47:07
     */
    private List<File> getStoreOrderExcels( String tempDir, int size, List<Map<String, Object>> list, int rowCache) throws Exception {
        //excel文件个数
        int n = list.size() / size + 1;
        FileDownloadUtils.createFile(tempDir);
        List<File> files = new ArrayList<File>();  //声明一个集合,用来存放多个Excel文件路径及名称

        for (int i = 0; i < n; i++) {
            int max = Math.min((i + 1) * size, list.size());
            //避免将需要合并的单元格拆分成两个表 , 如果最后的一条主订单数据等于下一条数据,max+1
            while (max < list.size() - 1 && list.get(max).get("orderNo").equals(list.get(max + 1).get("orderNo"))) {
                max++;
            }
            List<Map<String, Object>> partList = list.subList(i * size, max);
            SXSSFWorkbook wb = getStoreOrderExcel(partList, rowCache);
            //生成一个excel
            String path = tempDir + "\\商家订单数据-" + (i + 1) + ".xlsx";
            generateExcelToPath(wb, path);
            //excel添加到files中
            files.add(new File(path));
        }
        return files;
    }
2.3.1.1 generateExcelToPath方法,将生成excel到指定路径
/**
     * 生成excel到指定路径
     * @param wb
     * @param path
     * @throws Exception
     */
    private void generateExcelToPath(SXSSFWorkbook wb, String path) throws Exception {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(path);
            wb.write(fos);
        } finally {
            if (fos != null) {
                fos.flush();
                fos.close();
            }
            if (wb != null) {
                wb.close();
            }
        }
    }
2.3.2 downloadZip方法解析,打包下载zip
/**
     * 将批量文件打包下载成zip
     * @param request
     * @param response
     * @param zipName     下载的zip名
     * @param files       要打包的批量文件
     * @param zipPath     生成的zip路径
     * @throws Exception
     */
    private void downloadZip(HttpServletRequest request, HttpServletResponse response, String zipName, List<File> files, String zipPath)throws Exception {
        File srcfile[] = new File[files.size()];
        File zip = new File(zipPath);
        for (int i = 0; i < files.size(); i++) {
            srcfile[i] = files.get(i);
        }
        //生成.zip文件;
        FileInputStream inStream = null;
        ServletOutputStream os = null;
        try {
            //设置下载zip的头信息
            FileDownloadUtils.setZipDownLoadHeadInfo(response, request, zipName);
            os = response.getOutputStream();
            FileDownloadUtils.ZipFiles(srcfile, zip);
            inStream = new FileInputStream(zip);
            byte[] buf = new byte[4096];
            int readLength;
            while (((readLength = inStream.read(buf)) != -1)) {
                os.write(buf, 0, readLength);
            }
        }  finally {
            if (inStream != null) {
                inStream.close();
            }
            if (os != null) {
                os.flush();
                os.close();
            }
        }
    }

zip下载支线完毕

3.附 打成zip包

直接用工具类

/**
     * //压缩文件
     * @param srcfile   要压缩的文件数组
     * @param zipfile  生成的zip文件对象
     */
    public static void ZipFiles(java.io.File[] srcfile, File zipfile) throws Exception {
        byte[] buf = new byte[1024];
        FileOutputStream fos = new FileOutputStream(zipfile);
        ZipOutputStream out = new ZipOutputStream(fos);
        for (int i = 0; i < srcfile.length; i++) {
            FileInputStream in = new FileInputStream(srcfile[i]);
            out.putNextEntry(new ZipEntry(srcfile[i].getName()));
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            out.closeEntry();
            in.close();
        }
        out.close();
        fos.flush();
        fos.close();
    }

4. 删除临时文件夹

/**
     * 删除文件夹及文件夹下所有文件
     * @param dir
     * @return
     */
    public static boolean deleteDir(File dir) {
        if (dir == null || !dir.exists()){
            return true;
        }
        if (dir.isDirectory()) {
            String[] children = dir.list();
            //递归删除目录中的子目录下
            for (int i=0; i<children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空,可以删除
        return dir.delete();
    }

猜你喜欢

转载自blog.csdn.net/zzzgd_666/article/details/80804398
今日推荐