From 59c32acf7c18ab0a593c3edae3512793b631e148 Mon Sep 17 00:00:00 2001 From: zhangluping <1801075613@qq.com> Date: Sat, 28 Dec 2024 11:23:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demo/controller/ExportExcelController.java | 36 +++++++++++ .../com/example/demo/mapper/DetailYMapper.java | 25 ++++++-- .../com/example/demo/sevice/DataExportService.java | 71 ++++++++++++++++++++++ .../example/demo/sevice/ExportExcelService.java | 41 +++++++++++++ 4 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/example/demo/controller/ExportExcelController.java create mode 100644 src/main/java/com/example/demo/sevice/DataExportService.java create mode 100644 src/main/java/com/example/demo/sevice/ExportExcelService.java diff --git a/src/main/java/com/example/demo/controller/ExportExcelController.java b/src/main/java/com/example/demo/controller/ExportExcelController.java new file mode 100644 index 0000000..884a7ae --- /dev/null +++ b/src/main/java/com/example/demo/controller/ExportExcelController.java @@ -0,0 +1,36 @@ +package com.example.demo.controller; + +import com.example.demo.sevice.DataExportService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; + +@RestController +@RequestMapping("/export") +@RequiredArgsConstructor +@Transactional +@Slf4j +@CrossOrigin +public class ExportExcelController { + + @Autowired + private DataExportService dataExportService; + + @PostMapping("/exportUserExcel") + public void exportUserExcel(HttpServletResponse response) { + try { + dataExportService.exportData(response); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException("导出 Excel 文件失败", e); + } + } +} diff --git a/src/main/java/com/example/demo/mapper/DetailYMapper.java b/src/main/java/com/example/demo/mapper/DetailYMapper.java index c025cda..371bc1b 100644 --- a/src/main/java/com/example/demo/mapper/DetailYMapper.java +++ b/src/main/java/com/example/demo/mapper/DetailYMapper.java @@ -1,11 +1,9 @@ package com.example.demo.mapper; import com.example.demo.domain.entity.DetailY; +import com.example.demo.domain.entity.UserDetailExport; import com.example.demo.domain.vo.DetailYgold; -import org.apache.ibatis.annotations.Insert; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Options; -import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.*; import java.util.List; @@ -79,8 +77,8 @@ public interface DetailYMapper { "" }) DetailY getCount(DetailY detailY); + + //导出数据的SQL + @Select({ + "" + }) + List searchExport(@Param("offset") int offset, @Param("pageSize") int pageSize); + + @Select("SELECT COUNT(*) FROM detail_y") + int getTotalCount(); } diff --git a/src/main/java/com/example/demo/sevice/DataExportService.java b/src/main/java/com/example/demo/sevice/DataExportService.java new file mode 100644 index 0000000..66d5f15 --- /dev/null +++ b/src/main/java/com/example/demo/sevice/DataExportService.java @@ -0,0 +1,71 @@ +package com.example.demo.sevice; + +import com.example.demo.domain.entity.UserDetailExport; +import com.example.demo.mapper.DetailYMapper; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +@Service +public class DataExportService { + // 设置线程池 + private final ExecutorService executorService = Executors.newFixedThreadPool(3); // 使用10个线程的线程池 + + // 每次查询20w条数据 + private static final int PAGE_SIZE = 100000; + + @Autowired + private DetailYMapper detailYMapper; + + @Autowired + private ExportExcelService exportExcelService; + + public void exportData(HttpServletResponse response) throws IOException { + // 获取总记录数 + int totalRecords = detailYMapper.getTotalCount(); // 获取总记录数 + int totalPages = (totalRecords + PAGE_SIZE - 1) / PAGE_SIZE; // 计算总页 + // 设置Excel响应头 + exportExcelService.setExcelResponseProp(response, "userDetail"); + + // 创建一个集合用于收集所有查询结果 + List allData = new ArrayList<>(); + List>> futures = new ArrayList<>(); + + // 使用多线程导出数据 + for (int page = 0; page < totalPages; page++) { + final int currentPage = page; + futures.add(executorService.submit(() -> { + try { + return fetchData(currentPage, PAGE_SIZE); // 查询当前页的数据 + } catch (Exception e) { + e.printStackTrace(); + return new ArrayList(); // 出错时返回空数据 + } + })); + } + + // 等待所有任务完成,并收集结果 + for (Future> future : futures) { + try { + allData.addAll(future.get()); // 将每个线程的结果合并 + } catch (Exception e) { + e.printStackTrace(); + } + } + + // 一次性写入Excel + exportExcelService.writeDataToSheet(response, allData, 0); + } + + private List fetchData(int page, int pageSize) { + int offset = page * pageSize; // 计算偏移量 + return detailYMapper.searchExport(offset, pageSize); // 调用Mapper查询数据 + } +} diff --git a/src/main/java/com/example/demo/sevice/ExportExcelService.java b/src/main/java/com/example/demo/sevice/ExportExcelService.java new file mode 100644 index 0000000..2b6fdb1 --- /dev/null +++ b/src/main/java/com/example/demo/sevice/ExportExcelService.java @@ -0,0 +1,41 @@ +package com.example.demo.sevice; + +import com.alibaba.excel.EasyExcel; +import com.example.demo.domain.entity.UserDetailExport; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.List; + +@Service +public class ExportExcelService { + + // 设置响应头以导出 Excel 文件 + public void setExcelResponseProp(HttpServletResponse response, String fileName) throws IOException { + response.setContentType("application/vnd.ms-excel"); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx"); + } + + // 使用 EasyExcel 写入数据到 Excel + public void writeDataToSheet(HttpServletResponse response, List allData, int sheetIndex) throws IOException { + int batchSize = 2000000; // 每次写入200w条 + int totalDataSize = allData.size(); + int currentSheetIndex = sheetIndex; // 用来区分不同的Sheet + + for (int i = 0; i < totalDataSize; i += batchSize) { + int endIndex = Math.min(i + batchSize, totalDataSize); + List batchData = allData.subList(i, endIndex); + + // 每次写入一个新的 Sheet,Sheet 名称使用 currentSheetIndex + EasyExcel.write(response.getOutputStream(), UserDetailExport.class) + .sheet("Sheet " + (currentSheetIndex + 1)) // 设置Sheet名称 + .doWrite(batchData); // 写入当前批次的数据 + + currentSheetIndex++; // 增加 Sheet 索引 + } + } +} + +