本文目录导读:

我来为您提供几个Java文件导出的提速案例及优化方案:
大数据量Excel导出优化
优化前 - 使用POI SXSSFWorkbook(慢)
// 慢的方式:全部数据在内存中处理
public void exportSlow(List<Data> dataList, OutputStream out) {
try (XSSFWorkbook workbook = new XSSFWorkbook()) {
XSSFSheet sheet = workbook.createSheet("数据");
for (int i = 0; i < dataList.size(); i++) {
XSSFRow row = sheet.createRow(i);
Data data = dataList.get(i);
row.createCell(0).setCellValue(data.getId());
row.createCell(1).setCellValue(data.getName());
// ... 更多字段
}
workbook.write(out);
}
}
优化后 - 使用SXSSFWorkbook(流式写入)
public void exportFast(List<Data> dataList, OutputStream out) {
// SXSSFWorkbook:流式写入,内存中只保留100行
SXSSFWorkbook workbook = new SXSSFWorkbook(100);
SXSSFSheet sheet = workbook.createSheet("数据");
// 批量写入
int rowNum = 0;
for (Data data : dataList) {
SXSSFRow row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(data.getId());
row.createCell(1).setCellValue(data.getName());
}
// 写入磁盘
workbook.write(out);
workbook.dispose(); // 清理临时文件
}
多线程并发导出
public class ConcurrentExportService {
public void exportLargeData(List<Integer> ids, OutputStream out) {
// 分片处理
int batchSize = 1000;
List<List<Integer>> batches = partition(ids, batchSize);
// 使用线程池并行处理
ExecutorService executor = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors()
);
List<CompletableFuture<byte[]>> futures = new ArrayList<>();
for (List<Integer> batch : batches) {
CompletableFuture<byte[]> future = CompletableFuture.supplyAsync(() -> {
return processBatch(batch);
}, executor);
futures.add(future);
}
// 合并结果
try {
// 使用临时文件存储
Path tempFile = Files.createTempFile("export", ".csv");
try (FileOutputStream fos = new FileOutputStream(tempFile.toFile())) {
for (CompletableFuture<byte[]> future : futures) {
byte[] data = future.get();
fos.write(data);
fos.flush();
}
}
// 复制到输出流
Files.copy(tempFile, out);
// 清理临时文件
Files.deleteIfExists(tempFile);
} catch (Exception e) {
// 异常处理
} finally {
executor.shutdown();
}
}
private byte[] processBatch(List<Integer> ids) {
// 查询数据库获取数据
List<Data> dataList = queryData(ids);
// 转换为CSV格式
StringBuilder sb = new StringBuilder();
for (Data data : dataList) {
sb.append(data.getId()).append(",")
.append(data.getName()).append(",")
.append(data.getValue()).append("\n");
}
return sb.toString().getBytes(StandardCharsets.UTF_8);
}
}
文件分批写入(CSV示例)
public class BatchFileExport {
private static final int BATCH_SIZE = 5000;
public void exportLargeCsv(List<Data> dataList, OutputStream out) {
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(out, StandardCharsets.UTF_8))) {
// 写入表头
writer.write("ID,Name,Value,Date");
writer.newLine();
// 分批写入
int counter = 0;
for (Data data : dataList) {
StringBuilder line = new StringBuilder();
line.append(data.getId()).append(",")
.append(escapeCsv(data.getName())).append(",")
.append(data.getValue()).append(",")
.append(data.getDate());
writer.write(line.toString());
writer.newLine();
counter++;
// 每批刷新一次缓冲区
if (counter % BATCH_SIZE == 0) {
writer.flush();
}
}
writer.flush();
} catch (IOException e) {
throw new RuntimeException("导出失败", e);
}
}
private String escapeCsv(String value) {
if (value == null) return "";
if (value.contains(",") || value.contains("\"") || value.contains("\n")) {
return "\"" + value.replace("\"", "\"\"") + "\"";
}
return value;
}
}
使用NIO零拷贝(大文件复制)
public class ZeroCopyExport {
public void exportUsingNIO(File source, OutputStream out) throws IOException {
try (FileChannel sourceChannel = new FileInputStream(source).getChannel();
WritableByteChannel targetChannel = Channels.newChannel(out)) {
// 使用transferTo进行零拷贝
long position = 0;
long size = sourceChannel.size();
while (position < size) {
long transferred = sourceChannel.transferTo(
position,
Math.min(size - position, 1024 * 1024 * 10), // 每次10MB
targetChannel
);
position += transferred;
}
}
}
}
数据库分页+游标查询
public class CursorExportService {
public void exportWithCursor(OutputStream out) {
// 使用JDBC游标或MyBatis游标
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.SIMPLE)) {
DataMapper mapper = session.getMapper(DataMapper.class);
// 使用游标查询,避免内存溢出
Cursor<Data> cursor = mapper.scanAllData();
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(out, StandardCharsets.UTF_8))) {
writer.write("ID,Name,Value\n");
for (Data data : cursor) {
writer.write(String.format("%d,%s,%s\n",
data.getId(), data.getName(), data.getValue()));
writer.flush(); // 定期刷新
}
}
}
}
}
压缩输出(减少传输时间)
public class CompressedExport {
public void exportCompressed(List<Data> dataList, OutputStream out) {
try (GZIPOutputStream gzipOut = new GZIPOutputStream(out);
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(gzipOut, StandardCharsets.UTF_8))) {
// 写入数据
for (Data data : dataList) {
writer.write(String.format("%s,%s,%s\n",
data.getId(), data.getName(), data.getValue()));
}
writer.flush();
gzipOut.finish();
} catch (IOException e) {
throw new RuntimeException("导出失败", e);
}
}
// 使用ZIP格式支持多个文件
public void exportZip(Map<String, List<Data>> dataMap, OutputStream out) {
try (ZipOutputStream zos = new ZipOutputStream(out)) {
for (Map.Entry<String, List<Data>> entry : dataMap.entrySet()) {
ZipEntry zipEntry = new ZipEntry(entry.getKey() + ".csv");
zos.putNextEntry(zipEntry);
StringBuilder content = new StringBuilder();
for (Data data : entry.getValue()) {
content.append(data.getId()).append(",")
.append(data.getName()).append("\n");
}
zos.write(content.toString().getBytes(StandardCharsets.UTF_8));
zos.closeEntry();
}
} catch (IOException e) {
throw new RuntimeException("导出失败", e);
}
}
}
性能测试对比
public class ExportPerformanceTest {
public static void main(String[] args) {
List<Data> testData = generateTestData(100000); // 10万条数据
// 测试不同方式
testExport("传统POI", () -> {
// 原始方式
exportWithXSSF(testData);
});
testExport("SXSSF流式", () -> {
// 优化方式
exportWithSXSSF(testData);
});
testExport("多线程CSV", () -> {
// 多线程方式
exportWithMultiThread(testData);
});
}
private static void testExport(String name, Runnable task) {
long start = System.currentTimeMillis();
task.run();
long end = System.currentTimeMillis();
System.out.println(name + " 耗时: " + (end - start) + "ms");
}
}
-
选择合适的技术:
- 小数据量 (<1万):POI XSSFWorkbook
- 大数据量 (>1万):SXSSFWorkbook或CSV
- 超大文件:使用CSV + 压缩
-
使用流式处理:
- 避免一次性加载所有数据到内存
- 批量写入并定期刷新
-
并行处理:
- 利用多核CPU进行数据准备
- 分片查询数据库
-
减少I/O操作:
- 使用BufferedWriter缓冲
- 控制刷新频率
-
压缩输出:
使用GZIP或ZIP压缩减少网络传输时间
-
网络传输优化:
- 使用NIO零拷贝
- 分块传输
您可以根据具体的业务场景和数据量选择合适的优化方案。