Java文件导出提速案例怎么做

wen java案例 28

本文目录导读:

Java文件导出提速案例怎么做

  1. 大数据量Excel导出优化
  2. 多线程并发导出
  3. 文件分批写入(CSV示例)
  4. 使用NIO零拷贝(大文件复制)
  5. 数据库分页+游标查询
  6. 压缩输出(减少传输时间)
  7. 性能测试对比

我来为您提供几个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. 选择合适的技术

    • 小数据量 (<1万):POI XSSFWorkbook
    • 大数据量 (>1万):SXSSFWorkbook或CSV
    • 超大文件:使用CSV + 压缩
  2. 使用流式处理

    • 避免一次性加载所有数据到内存
    • 批量写入并定期刷新
  3. 并行处理

    • 利用多核CPU进行数据准备
    • 分片查询数据库
  4. 减少I/O操作

    • 使用BufferedWriter缓冲
    • 控制刷新频率
  5. 压缩输出

    使用GZIP或ZIP压缩减少网络传输时间

  6. 网络传输优化

    • 使用NIO零拷贝
    • 分块传输

您可以根据具体的业务场景和数据量选择合适的优化方案。

抱歉,评论功能暂时关闭!