java实现大数量级的数据导出excel,excel数据读取

先搞个工具类:

public class EasyExcelUtil {
    /**
     * @param response
     * @param exportTypeEnum
     * @param fileName
     * @return java.io.OutputStream
     * @Author JH
     * @Description 设置导出响应请求头
     * @Date 13:57 2022/10/27
     **/
    public static OutputStream getOutputStream(HttpServletResponse response, ExportTypeEnum exportTypeEnum, String fileName) throws IOException {
        response.setContentType(exportTypeEnum.getContentType());
        response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache");
        response.setCharacterEncoding(Constants.UTF8);
        // 这里URLEncoder.encode可以防止中文乱码
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName + exportTypeEnum.getFileExtName()).getBytes()));
        response.setHeader("download-filename", fileName + exportTypeEnum.getFileExtName());
        return response.getOutputStream();
    }

    /**
     * @param templateEmun
     * @return java.io.InputStream
     * @Author JH
     * @Description 获取模板 的 InputStream
     * @Date 14:09 2022/10/27
     **/
    public static InputStream getTemplate(TemplateEmun templateEmun) {
        String templateName = templateEmun.getName() + templateEmun.getExportTypeEnum().getFileExtName();
        ClassLoader classLoader = EasyExcelUtil.class.getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream("/template/" + templateName);
        if (Objects.isNull(inputStream)) {
            inputStream = classLoader.getResourceAsStream("template/" + templateName);
        }
/*        if (Objects.isNull(inputStream)) {
            throw new TemplateNotFoundException("下载模板没有找到,请核对后重试");
        }*/
        Assert.isTrue(StringUtils.isNotNull(inputStream), "下载模板没有找到,请核对后重试");
        return inputStream;
    }



    /**
     *
     * @Author ljy
     * @Description 优化后的导出方法,支持高性能导出
     * @Date 16:02 2024/9/9
     * @param response
     * @param templateEmun
     * @param fileName
     * @param exportList
     **/
    public static void exportExcelBetter(HttpServletResponse response, TemplateEmun templateEmun, String fileName, List exportList) throws IOException {
        // 使用流式写入
        OutputStream outputStream = getOutputStream(response, templateEmun.getExportTypeEnum(), fileName);
        // 关闭自动流关闭
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(outputStream)
                .registerConverter(new ExcelLocalDateTimeConverter())
                .registerConverter(new ExcelLocalDateConverter())
                .withTemplate(getTemplate(templateEmun))
                .autoCloseStream(Boolean.FALSE);
        ExcelWriter excelWriter = excelWriterBuilder.build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        // 分批写入数据
        // 每批写入的数量
        int batchSize = 5000;
        for (int i = 0; i < exportList.size(); i += batchSize) {
            int end = Math.min(i + batchSize, exportList.size());
            List<?> batchList = exportList.subList(i, end);
            excelWriter.fill(batchList, fillConfig, writeSheet);
        }
        // 手动关闭流
        excelWriter.finish();
        outputStream.close();
    }



   /**
    *
     * @Author ljy
     * @Description 
     * @Date 14:40 2024/9/12 
     * @param file
     * @param templateEmun
     * @param fileName
     * @param exportList 
     **/
    public static void exportExcelFile(File file, TemplateEmun templateEmun, String fileName, List exportList) throws IOException {
        // 使用流式写入
        OutputStream outputStream = new FileOutputStream(file);
        // 关闭自动流关闭
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(outputStream)
                .registerConverter(new ExcelLocalDateTimeConverter())
                .registerConverter(new ExcelLocalDateConverter())
                .withTemplate(getTemplate(templateEmun))
                .autoCloseStream(Boolean.FALSE);
        ExcelWriter excelWriter = excelWriterBuilder.build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        // 分批写入数据
/*        int batchSize = 5000;
        for (int i = 0; i < exportList.size(); i += batchSize) {
            int end = Math.min(i + batchSize, exportList.size());
            List<?> batchList = exportList.subList(i, end);

        }*/
        excelWriter.fill(exportList, fillConfig, writeSheet);
        // 手动关闭流
        excelWriter.finish();
        outputStream.close();
    }


    /**
     * @param response
     * @param templateEmun
     * @param fileName   下载文件名
     * @param exportList 导出的数据
     * @Author JH
     * @Description
     * @Date 15:56 2022/10/27
     **/
    public static void exportExcel(HttpServletResponse response, TemplateEmun templateEmun, String fileName, List exportList) throws IOException {
        exportExcel(response, null, templateEmun, fileName, exportList);
    }

    /**
     * @param response
     * @param writeHandler 单元格处理类
     * @param templateEmun
     * @param fileName     下载文件名
     * @param exportList   导出的数据
     * @Author JH
     * @Description
     * @Date 15:56 2022/10/27
     **/
    public static void exportExcel(HttpServletResponse response, WriteHandler writeHandler, TemplateEmun templateEmun, String fileName, List exportList) throws IOException {
        OutputStream outputStream = getOutputStream(response, templateEmun.getExportTypeEnum(), fileName);
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(outputStream).registerConverter(new ExcelLocalDateTimeConverter()).registerConverter(new ExcelLocalDateConverter())
                .withTemplate(getTemplate(templateEmun))
                .autoCloseStream(Boolean.TRUE);
        if (writeHandler != null) {
            excelWriterBuilder.registerWriteHandler(writeHandler);
        }

        ExcelWriter excelWriter = excelWriterBuilder.build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        excelWriter.fill(exportList, fillConfig, writeSheet);
        excelWriter.finish();
    }

    /**
     * @param inputstream
     * @param tclass
     * @param rowNum      表头所在行
     * @return java.util.List<T>
     * @Author JH
     * @Description
     * @Date 16:50 2022/10/27
     **/
    public static <T> List<T> readExcel(InputStream inputstream, Class<T> tclass, int rowNum) throws InstantiationException, IllegalAccessException {
        return EasyExcel.read(inputstream, tclass, new ReadExcelListener(tclass)).head(tclass).registerConverter(new ExcelLocalDateTimeConverter())
                .sheet().headRowNumber(rowNum).doReadSync();
    }


    /**
     *
     * @Author ljy
     * @Description 分批次读取
     * @Date 10:37 2024/9/11
     * @param inputstream
     * @param tclass
     * @param rowNum
     * @return java.util.List<T>
     **/
    public static <T> List<T> readExcelBatch(InputStream inputstream, Class<T> tclass, int rowNum) throws InstantiationException, IllegalAccessException {
        List<T> resultList = new ArrayList<>();
        List<T> all = new ArrayList<>();
        EasyExcel.read(inputstream, tclass, new ReadExcelListener<T>(tclass) {
            @Override
            public void invoke(T data, AnalysisContext context) {
                resultList.add(data);
                if (resultList.size() >= 5000) {
                    // 处理当前批次数据
                    processBatch(resultList, all);
                    resultList.clear();
                }
            }
        })
                .head(tclass)
                .registerConverter(new ExcelLocalDateTimeConverter())
                .sheet()
                .headRowNumber(rowNum)
                .doRead();
        // 处理剩余数据
        if (!resultList.isEmpty()) {
            processBatch(resultList, all);
        }
        return all;
    }

    private static <T> void processBatch(List<T> resultList, List<T> all) {
        all.addAll(resultList);
    }


    /**
     * @Author lx
     * @Date 上午10:01 2024/8/5
     * @param inputstream
     * @param tclass
     * @param headerRowNumber 表头所在行
     * @param startRow  数据所在行
     * @return java.util.List<T>
     **/
    public static <T> List<T> readExcel(InputStream inputstream, Class<T> tclass, int headerRowNumber, int startRow) throws InstantiationException, IllegalAccessException {
        ReadExcelListener readExcelListener = new ReadExcelListener(tclass, headerRowNumber, startRow);
        EasyExcel.read(inputstream).headRowNumber(headerRowNumber).head(tclass).registerReadListener(readExcelListener).registerConverter(new ExcelLocalDateTimeConverter())
        .sheet().doReadSync();
        return readExcelListener.getList();
    }

    /**
     * @param inputstream
     * @param tclass
     * @param rowNum      表头所在行
     * @param sheetName   sheet名称
     * @return java.util.List<T>
     * @Author JH
     * @Description
     * @Date 16:50 2022/10/27
     **/
    public static <T> List<T> readExcelSheet(InputStream inputstream, Class<T> tclass, int rowNum, String sheetName) throws InstantiationException, IllegalAccessException {
        return EasyExcel.read(inputstream).headRowNumber(rowNum).head(tclass).registerReadListener(new ReadExcelListener(tclass, rowNum - 1)).registerConverter(new ExcelLocalDateTimeConverter())
                .sheet(sheetName).doReadSync();
    }

现在有一个这样的需求:根据指定一个所属地市的所有电缆数据导出成文件(电缆的数据量达到10万级别哦)

对于这个需求要注意的地方是:

具体实现的思路,先查询出指定所属地市数据,然后再excelWriter.fill填充到excel表格,最后响应。

对于庞大的数据量接口要保证接口响应不能太久,要避免OOM

因此在实现过程中可以采用分批次查询,分批次写入excel表格,最后将每个批次的excel文件放到一个压缩文件夹里面,最后一起再一起响应。

分析工具类里面的方法:方法的主要目的是将一个数据列表导出为一个 Excel 文件,使用指定的模板和格式化选项。它通过流式写入的方式高效地处理数据,并确保在完成后正确关闭资源。

    public static void exportExcelFile(File file, TemplateEmun templateEmun, String fileName, List exportList) throws IOException {
        // 使用流式写入
        OutputStream outputStream = new FileOutputStream(file);
        // 关闭自动流关闭
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(outputStream)
                .registerConverter(new ExcelLocalDateTimeConverter())
                .registerConverter(new ExcelLocalDateConverter())
                .withTemplate(getTemplate(templateEmun))
                .autoCloseStream(Boolean.FALSE);
        ExcelWriter excelWriter = excelWriterBuilder.build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        excelWriter.fill(exportList, fillConfig, writeSheet);
        // 手动关闭流
        excelWriter.finish();
        outputStream.close();
    }
  1. 创建输出流:

    OutputStream outputStream = new FileOutputStream(file);
    
    • 使用 FileOutputStream 创建一个输出流,以便将数据写入指定的 Excel 文件。
  2. 构建 ExcelWriter:

    ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(outputStream)
            .registerConverter(new ExcelLocalDateTimeConverter())
            .registerConverter(new ExcelLocalDateConverter())
            .withTemplate(getTemplate(templateEmun))
            .autoCloseStream(Boolean.FALSE);
    
    • 使用 EasyExcel 库的 write 方法创建一个 ExcelWriterBuilder
    • 注册了两个转换器:ExcelLocalDateTimeConverter 和 ExcelLocalDateConverter,用于处理日期和时间格式。
    • 使用指定的模板(通过 getTemplate(templateEmun) 获取)来格式化 Excel 文件。
    • 设置 autoCloseStream 为 false,表示在写入完成后不会自动关闭输出流。
  3. 构建写入工作表:

    WriteSheet writeSheet = EasyExcel.writerSheet().build();
    
    • 创建一个 WriteSheet 对象,表示要写入的工作表。
  4. 配置填充设置:

    FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
    
    • 创建一个 FillConfig 对象,设置 forceNewRow 为 true,表示在填充数据时强制换行。
  5. 填充数据:

    excelWriter.fill(exportList, fillConfig, writeSheet);
    
    • 使用 excelWriter 将 exportList 中的数据填充到指定的工作表中。
  6. 关闭流:

    excelWriter.finish();
    outputStream.close();
    
    • 调用 finish() 方法完成写入操作,并手动关闭输出流,确保资源被释放

具体业务使用:

 /**
    *
     * @Author ljy
     * @Description 数据导出成文件
     * @Date 14:54 2024/9/12
     * @param city      所属地市
     * @param type      选择要到处的数据类型
     * @param response  响应体
     **/
    @GetMapping("/export/all")
    public void exportDataToExcelAll(@RequestParam(value = "city", required = true)String city,
                                     @RequestParam(value = "type", required = true)String type,
                                     HttpServletResponse response) throws IOException {
        accountCableService.exportDataToExcelAll(city, type, response);
    }
public void exportDataToExcelAll(String city, String type, HttpServletResponse response) throws IOException {
        ExcelExportObjectEnum exportObjectEnum = ExcelExportObjectEnum.ofByCode(type);
        Assert.notNull(exportObjectEnum, "没找到该类型对应的模板");
        switch (exportObjectEnum){
            case CABLE:
                commonService.exportDataToExcelCable(city, response);
                break;
            case CABLE_SECTION:
                commonService.exportDataToExcelCableSection(city, response);
                break;
            case CABLE_TERMINAL:
                commonService.exportDataToExcelCableTerminal(city, response);
                break;
            case LINE_JOINT:
                commonService.exportDataToExcelCableLineJoint(city, response);
                break;
            case LINE:
                commonService.exportDataToExcelCableLine(city, response);
                break;
            case DKX:
                commonService.exportDataToExcelDkx(city, response);
                break;
            default:

        }
public void exportDataToExcelCable(String city, HttpServletResponse response) throws IOException {
        // 当前页码
        int pageNum = 1;
        int j = 0;
        int pageSize = 8000;
        ExportExcelRequestVO vo = new ExportExcelRequestVO();
        vo.setCity(city);
        vo.setPageNum(pageNum);
        vo.setPageSize(pageSize);
        PageResult<AccountCable> pageResult = iAccountCableCrud.findCableListAll(vo);
        long total = pageResult.getTotal();
        int pages = (int) Math.ceil((double) total / pageSize);
        // 创建一个临时文件用于存储压缩文件
        File zipFile = File.createTempFile("cables", ".zip");
        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) {
            for (int i = 1; i <= pages; i++) {
                vo.setPageNum(i);
                List<AccountCable> list = iAccountCableCrud.findCableListAll(vo).getData();
                // 创建每个Excel文件
                File tempExcelFile = File.createTempFile("cable_page_" + j, ".xlsx");
                EasyExcelUtil.exportExcelFile(tempExcelFile, TemplateEmun.CABLE_ACCOUNT_ALL_TEMPLATE, "电缆记录表", list);
                // 将Excel文件添加到压缩文件中
                try (FileInputStream fis = new FileInputStream(tempExcelFile)) {
                    ZipEntry zipEntry = new ZipEntry(tempExcelFile.getName());
                    zos.putNextEntry(zipEntry);
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = fis.read(buffer)) >= 0) {
                        zos.write(buffer, 0, length);
                    }
                    zos.closeEntry();
                }
                // 删除临时Excel文件
                tempExcelFile.delete();
                j++;
            }
        }
        // 设置响应头并发送压缩文件
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=cables.zip");
        try (FileInputStream fis = new FileInputStream(zipFile);
             OutputStream os = response.getOutputStream()) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = fis.read(buffer)) >= 0) {
                os.write(buffer, 0, length);
            }
        }
        // 删除临时压缩文件
        zipFile.delete();
    }

分析核心业务方法exportDataToExcelCable():

这个方法实现了从数据库中提取电缆记录数据,并将其导出为一个包含多个 Excel 文件的 ZIP 文件,供用户下载。

  1. 获取数据

    • 使用 iAccountCableCrud.findCableListAll(vo) 方法获取电缆记录的总数和分页信息。
  2. 创建临时 ZIP 文件

    • 创建一个临时 ZIP 文件,用于存储生成的 Excel 文件。
  3. 分页处理

    • 根据总记录数和每页大小计算总页数。
    • 循环遍历每一页,获取电缆记录列表。
  4. 生成 Excel 文件

    • 为每一页创建一个临时 Excel 文件,并使用 EasyExcelUtil.exportExcelFile 方法将数据写入该文件。
  5. 将 Excel 文件添加到 ZIP

    • 将生成的 Excel 文件读取并写入到 ZIP 文件中。
  6. 清理临时文件

    • 删除每个生成的临时 Excel 文件。
  7. 设置响应头并发送 ZIP 文件

    • 设置 HTTP 响应的内容类型和文件名。
    • 将 ZIP 文件的内容写入 HTTP 响应流中,发送给客户端。
  8. 删除临时 ZIP 文件

    • 在完成文件发送后,删除临时 ZIP 文件以释放资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值