4 changed files with 167 additions and 6 deletions
-
36src/main/java/com/example/demo/controller/ExportExcelController.java
-
25src/main/java/com/example/demo/mapper/DetailYMapper.java
-
71src/main/java/com/example/demo/sevice/DataExportService.java
-
41src/main/java/com/example/demo/sevice/ExportExcelService.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); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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<UserDetailExport> allData = new ArrayList<>(); |
||||
|
List<Future<List<UserDetailExport>>> 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<UserDetailExport>(); // 出错时返回空数据 |
||||
|
} |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
// 等待所有任务完成,并收集结果 |
||||
|
for (Future<List<UserDetailExport>> future : futures) { |
||||
|
try { |
||||
|
allData.addAll(future.get()); // 将每个线程的结果合并 |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 一次性写入Excel |
||||
|
exportExcelService.writeDataToSheet(response, allData, 0); |
||||
|
} |
||||
|
|
||||
|
private List<UserDetailExport> fetchData(int page, int pageSize) { |
||||
|
int offset = page * pageSize; // 计算偏移量 |
||||
|
return detailYMapper.searchExport(offset, pageSize); // 调用Mapper查询数据 |
||||
|
} |
||||
|
} |
@ -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<UserDetailExport> 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<UserDetailExport> batchData = allData.subList(i, endIndex); |
||||
|
|
||||
|
// 每次写入一个新的 Sheet,Sheet 名称使用 currentSheetIndex |
||||
|
EasyExcel.write(response.getOutputStream(), UserDetailExport.class) |
||||
|
.sheet("Sheet " + (currentSheetIndex + 1)) // 设置Sheet名称 |
||||
|
.doWrite(batchData); // 写入当前批次的数据 |
||||
|
|
||||
|
currentSheetIndex++; // 增加 Sheet 索引 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue