当前位置: 首页 > news >正文

Java实现大数据量导出报表

一、实现方式

在Java中,导出数据到Excel有多种方式,每种方式都有其优缺点,适用于不同的场景。以下是常见的几种方式及其特点:

1.1 Apache POI

Apache POI 是 Java 中最流行的库,支持读写 Excel 文件(包括 .xls 和 .xlsx 格式)。

  • 特点:
    • 支持 .xls(HSSFWorkbook)和 .xlsx(XSSFWorkbook、SXSSFWorkbook)。
    • 功能强大,支持样式、公式、图表等。
    • SXSSFWorkbook 支持流式写入,适合大数据量导出。
  • 适用场景:
    • 需要导出复杂格式的 Excel 文件。
    • 大数据量导出(使用 SXSSFWorkbook)。
1.2 EasyExcel

EasyExcel 是阿里巴巴开源的 Excel 操作库,基于 Apache POI 封装,专注于大数据量导出和导入。

  • 特点:
    • 支持流式读写,内存占用低。
    • API 简单易用。
    • 支持 .xlsx 格式。
  • 适用场景:
    • 大数据量导出(百万级数据)。
    • 需要高性能和低内存占用的场景。
1.3 OpenCSV

虽然不是直接导出 Excel 文件,但 CSV 文件可以被 Excel 直接打开,适合简单的数据导出。

  • 特点:
    • 轻量级,速度快。
    • 文件格式简单,不支持样式、公式等。
    • 适合纯数据导出。
  • 适用场景:
    • 数据量较大,且不需要复杂格式。
    • 需要快速导出和导入。

二、使用SXSSFWorkbook

2.1 添加依赖

在pom.xml中添加Apache POI依赖:

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.3</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.1.2</version>
</dependency>
2.2 定义数据模型

创建一个 Java 类表示导出的数据模型。

public class DataModel {
    private Long id;
    private String name;
    private Double value;

    // 构造方法、Getter 和 Setter
    public DataModel(Long id, String name, Double value) {
        this.id = id;
        this.name = name;
        this.value = value;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getValue() {
        return value;
    }

    public void setValue(Double value) {
        this.value = value;
    }
}
2.3 分页查询数据

使用Spring Data JPA或MyBatis进行分页查询。

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
public class DataService {

    @Autowired
    private DataRepository dataRepository;

    public Page<DataEntity> getDataByPage(int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return dataRepository.findAll(pageable);
    }
}
2.4 多线程导出数据

使用线程池并行处理分页查询和数据写入。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.*;

@Service
public class ExcelExportService {

    @Autowired
    private DataService dataService;

    // 线程池配置
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    public void exportLargeDataToExcel(String filePath, int pageSize) throws IOException, InterruptedException, ExecutionException {
        // 创建 SXSSFWorkbook,设置内存中保留的行数(默认100)
        try (SXSSFWorkbook workbook = new SXSSFWorkbook(100)) {
            Sheet sheet = workbook.createSheet("Sheet1");

            // 创建表头
            Row headerRow = sheet.createRow(0);
            headerRow.createCell(0).setCellValue("ID");
            headerRow.createCell(1).setCellValue("Name");
            headerRow.createCell(2).setCellValue("Value");

            // 计算总页数
            long totalCount = dataService.getTotalCount();
            int totalPages = (int) Math.ceil((double) totalCount / pageSize);

            // 使用 CountDownLatch 等待所有线程完成
            CountDownLatch latch = new CountDownLatch(totalPages);

            // 提交任务到线程池
            for (int page = 0; page < totalPages; page++) {
                final int currentPage = page;
                executorService.submit(() -> {
                    try {
                        // 分页查询数据
                        Page<DataModel> dataPage = dataService.getDataByPage(currentPage, pageSize);
                        List<DataModel> dataList = dataPage.getContent();

                        // 写入当前页数据
                        synchronized (sheet) {
                            int startRow = currentPage * pageSize + 1; // 数据从第2行开始
                            for (int i = 0; i < dataList.size(); i++) {
                                DataModel data = dataList.get(i);
                                Row row = sheet.createRow(startRow + i);
                                row.createCell(0).setCellValue(data.getId());
                                row.createCell(1).setCellValue(data.getName());
                                row.createCell(2).setCellValue(data.getValue());
                            }
                        }
                    } finally {
                        latch.countDown(); // 任务完成
                    }
                });
            }

            // 等待所有线程完成
            latch.await();

            // 写入文件
            try (FileOutputStream outputStream = new FileOutputStream(filePath)) {
                workbook.write(outputStream);
            }

            // 清理临时文件
            workbook.dispose();
        }

        // 关闭线程池
        executorService.shutdown();
    }
}
2.5 调用导出方法

在Controller或Service中调用导出方法。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.concurrent.ExecutionException;

@RestController
@RequestMapping("/export")
public class ExportController {

    @Autowired
    private ExcelExportService excelExportService;

    @GetMapping("/excel")
    public String exportToExcel() throws IOException, InterruptedException, ExecutionException {
        String filePath = "large_data_export.xlsx";
        excelExportService.exportLargeDataToExcel(filePath, 10000); // 每页查询10000条数据
        return "Export successful! File saved at: " + filePath;
    }
}

三、使用EasyExcel

3.1 添加依赖

在 pom.xml 中添加 EasyExcel 依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
</dependency>
3.2 定义数据模型

创建一个 Java 类表示导出的数据模型。

import com.alibaba.excel.annotation.ExcelProperty;

public class DataModel {
    @ExcelProperty("ID")
    private Long id;

    @ExcelProperty("Name")
    private String name;

    @ExcelProperty("Value")
    private Double value;

    // 构造方法、Getter 和 Setter
    public DataModel(Long id, String name, Double value) {
        this.id = id;
        this.name = name;
        this.value = value;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getValue() {
        return value;
    }

    public void setValue(Double value) {
        this.value = value;
    }
}
3.3 分页查询数据

使用 Spring Data JPA 或 MyBatis 进行分页查询。

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
public class DataService {

    @Autowired
    private DataRepository dataRepository;

    public Page<DataModel> getDataByPage(int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return dataRepository.findAll(pageable);
    }
}
3.4 多线程导出数据

使用线程池并行处理分页查询和数据写入。

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.WriteSheet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.*;

@Service
public class ExcelExportService {

    @Autowired
    private DataService dataService;

    // 线程池配置
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    public void exportLargeDataToExcel(HttpServletResponse response, int pageSize) throws IOException, InterruptedException, ExecutionException {
        // 设置响应头
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String fileName = "large_data_export.xlsx";
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        // 创建 Excel 写入器
        WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").head(DataModel.class).build();

        // 计算总页数
        int totalPages = (int) Math.ceil((double) dataService.getTotalCount() / pageSize);

        // 使用 CountDownLatch 等待所有线程完成
        CountDownLatch latch = new CountDownLatch(totalPages);

        // 提交任务到线程池
        for (int page = 0; page < totalPages; page++) {
            final int currentPage = page;
            executorService.submit(() -> {
                try {
                    // 分页查询数据
                    Page<DataModel> dataPage = dataService.getDataByPage(currentPage, pageSize);
                    List<DataModel> dataList = dataPage.getContent();

                    // 写入当前页数据
                    EasyExcel.write(response.getOutputStream(), DataModel.class)
                            .sheet("Sheet1")
                            .doWrite(dataList);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown(); // 任务完成
                }
            });
        }

        // 等待所有线程完成
        latch.await();

        // 关闭线程池
        executorService.shutdown();
    }
}
3.5 Controller 调用导出方法

在 Controller 中调用导出方法。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ExecutionException;

@RestController
@RequestMapping("/export")
public class ExportController {

    @Autowired
    private ExcelExportService excelExportService;

    @GetMapping("/excel")
    public void exportToExcel(HttpServletResponse response) throws IOException, InterruptedException, ExecutionException {
        excelExportService.exportLargeDataToExcel(response, 10000); // 每页查询10000条数据
    }
}

四、使用OpenCSV

4.1 添加依赖

在 pom.xml 中添加 OpenCSV 依赖:

<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.8</version>
</dependency>
4.2 定义数据模型

创建一个 Java 类表示导出的数据模型。

public class DataModel {
    private Long id;
    private String name;
    private Double value;

    // 构造方法、Getter 和 Setter
    public DataModel(Long id, String name, Double value) {
        this.id = id;
        this.name = name;
        this.value = value;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getValue() {
        return value;
    }

    public void setValue(Double value) {
        this.value = value;
    }
}
4.3 分页查询数据

使用 Spring Data JPA 或 MyBatis 进行分页查询。

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
public class DataService {

    @Autowired
    private DataRepository dataRepository;

    public Page<DataModel> getDataByPage(int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return dataRepository.findAll(pageable);
    }

    public long getTotalCount() {
        return dataRepository.count();
    }
}
4.4 多线程导出数据

使用线程池并行处理分页查询和数据写入。

import com.opencsv.CSVWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

@Service
public class CsvExportService {

    @Autowired
    private DataService dataService;

    // 线程池配置
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    public void exportLargeDataToCsv(String filePath, int pageSize) throws IOException, InterruptedException, ExecutionException {
        // 创建 CSV 写入器
        try (CSVWriter writer = new CSVWriter(new FileWriter(filePath))) {
            // 写入表头
            writer.writeNext(new String[]{"ID", "Name", "Value"});

            // 计算总页数
            long totalCount = dataService.getTotalCount();
            int totalPages = (int) Math.ceil((double) totalCount / pageSize);

            // 使用 Future 保存每个线程的查询结果
            List<Future<List<DataModel>>> futures = new ArrayList<>();

            // 提交任务到线程池
            for (int page = 0; page < totalPages; page++) {
                final int currentPage = page;
                Future<List<DataModel>> future = executorService.submit(() -> {
                    Page<DataModel> dataPage = dataService.getDataByPage(currentPage, pageSize);
                    return dataPage.getContent();
                });
                futures.add(future);
            }

            // 合并所有线程的查询结果并写入 CSV
            for (Future<List<DataModel>> future : futures) {
                List<DataModel> dataList = future.get();
                for (DataModel data : dataList) {
                    writer.writeNext(new String[]{
                            String.valueOf(data.getId()),
                            data.getName(),
                            String.valueOf(data.getValue())
                    });
                }
            }
        }

        // 关闭线程池
        executorService.shutdown();
    }
}
4.5 Controller 调用导出方法

在 Controller 中调用导出方法。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.concurrent.ExecutionException;

@RestController
@RequestMapping("/export")
public class ExportController {

    @Autowired
    private CsvExportService csvExportService;

    @GetMapping("/csv")
    public String exportToCsv() throws IOException, InterruptedException, ExecutionException {
        String filePath = "large_data_export.csv";
        csvExportService.exportLargeDataToCsv(filePath, 10000); // 每页查询10000条数据
        return "Export successful! File saved at: " + filePath;
    }
}

相关文章:

  • Select 下拉菜单选项分组
  • 面试基础----Spring Cloud 微服务架构中的熔断降级:Hystrix 与 Resilience4j 解析
  • 以影像技术重构智能座舱体验,开启驾乘互动新纪元
  • RK3588V2--ES8388声卡适配记录
  • Leetcode---209长度最小子数组
  • 代码贴——堆(二叉树)数据结构
  • 智能对讲机:5G+AI赋能下的石油工业新“声”态
  • linux top htop 命令有什么不同
  • vue These dependencies were not found
  • 【mysql】mysql数据库数据导入、导出/备份还原操作
  • 16.1STM32_ADC
  • 微软AI900认证备考全攻略:开启AI职业进阶之路
  • android13打基础:控件datepicker
  • 【代码分享】基于IRM和RRT*的无人机路径规划方法详解与Matlab实现
  • 中科大 计算机网络组成原理 1.4 接入网和物理媒体 笔记
  • 公开笔记:Python语法和常用函数介绍
  • C语音的常见概念
  • C语言-----扫雷游戏
  • PHP fastadmin 学习
  • 【CPP面经】科大讯飞 腾讯后端开发面经分享
  • 公厂做网站需要开诚信通吗/软件测试培训班多少钱
  • 阿里云上做网站套模板怎么做/优化设计全部答案
  • 西安电子商务网站/seo外链推广平台
  • 动漫网站建设方案设计/新网seo关键词优化教程
  • 网站建设源文件/app推广是什么工作
  • 西安网站建设公司有哪些/平台推广方式