当前位置:首页 > 后端开发 > 正文

java怎么生成导出

Java可通过Apache POI库生成Excel,或使用iText/PDFBox生成PDF,需添加对应依赖,创建工作簿/文档对象,填充数据

Java开发中,数据导出是常见需求,通常涉及将数据库查询结果、集合对象或业务数据转换为可下载的文件格式(如Excel、CSV、PDF等),以下是完整的实现方案及技术要点解析:


核心思路与技术选型

目标格式 推荐库 适用场景 优势
Excel Apache POI / EasyExcel 复杂表格、公式、图表 功能强大,支持.xls/.xlsx
CSV OpenCSV / Jackson/Gson 轻量化文本交换 跨平台兼容,解析速度快
PDF iText / PDFBox 固定排版文档 可视化效果好,打印友好
HTML Thymeleaf/Freemarker 邮件模板、网页预览 灵活渲染,结合前端样式

️ 关键注意事项:

  1. 内存管理:百万级数据需采用SAX模式(事件驱动)而非DOM全量加载
  2. 编码规范:中文字符必须使用UTF-8编码,避免乱码
  3. 安全性:禁止直接写入绝对路径,建议通过ServletOutputStream传输
  4. 性能优化:大数据量建议分批次处理(每批5000条),异步任务队列更佳

主流导出方案详解

方案1:Excel导出(Apache POI + Spring Boot)

<!-Maven依赖 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.3</version>
</dependency>

核心代码实现

public void exportToExcel(HttpServletResponse response, List<User> dataList) throws IOException {
    // 1. 创建工作簿 & 工作表
    Workbook workbook = new XSSFWorkbook(); // .xlsx格式
    Sheet sheet = workbook.createSheet("用户列表");
    // 2. 创建表头样式
    CellStyle headerStyle = workbook.createCellStyle();
    headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
    headerStyle.setAlignment(HorizontalAlignment.CENTER);
    // 3. 写入表头
    Row headerRow = sheet.createRow(0);
    String[] titles = {"ID", "姓名", "年龄", "注册时间"};
    for (int i=0; i<titles.length; i++) {
        Cell cell = headerRow.createCell(i);
        cell.setCellValue(titles[i]);
        cell.setCellStyle(headerStyle);
    }
    // 4. 填充数据行
    CreationHelper createHelper = workbook.getCreationHelper();
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    for (int rowNum=1; rowNum<=dataList.size(); rowNum++) {
        User user = dataList.get(rowNum-1);
        Row row = sheet.createRow(rowNum);
        row.createCell(0).setCellValue(user.getId());
        row.createCell(1).setCellValue(user.getName());
        row.createCell(2).setCellValue(user.getAge());
        row.createCell(3).setCellValue(df.format(user.getRegisterTime()));
    }
    // 5. 自动调整列宽
    for (int i=0; i<titles.length; i++) {
        sheet.autoSizeColumn(i);
    }
    // 6. 设置响应头
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setHeader("Content-Disposition", "attachment;filename=users.xlsx");
    workbook.write(response.getOutputStream());
    workbook.close();
}

进阶技巧

  • 冻结首行:sheet.createFreezePane(0, 1, 0, 1)
  • 数字格式化:cell.setCellStyle(numberFormatStyle)配合DataFormat
  • 图片插入:Drawing drawing = sheet.createDrawingPatriarch();

方案2:CSV导出(OpenCSV)

<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.7.1</version>
</dependency>

核心代码实现

public void exportToCsv(HttpServletResponse response, List<User> dataList) throws Exception {
    // 1. 创建CSVWriter
    try (OutputStream os = response.getOutputStream();
         Writer writer = new OutputStreamWriter(os, StandardCharsets.UTF_8);
         CSVWriter csvWriter = new CSVWriter(writer)) {
        // 2. 写入表头
        String[] header = {"ID", "姓名", "年龄", "注册时间"};
        csvWriter.writeNext(header);
        // 3. 写入数据
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (User user : dataList) {
            String[] line = {
                String.valueOf(user.getId()),
                user.getName(),
                String.valueOf(user.getAge()),
                df.format(user.getRegisterTime())
            };
            csvWriter.writeNext(line);
        }
    }
    // 4. 设置响应头
    response.setContentType("text/csv;charset=UTF-8");
    response.setHeader("Content-Disposition", "attachment;filename=users.csv");
}

特殊处理

  • 字段含逗号:用双引号包裹 """"""text,with,commas"""
  • 换行符处理:启用quoteChar参数,默认为
  • BOM头添加:writer.write('uFEFF');解决Excel打开乱码

方案3:PDF导出(iText7)

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.2.3</version>
</dependency>

核心代码实现

public void exportToPdf(HttpServletResponse response, List<User> dataList) throws IOException {
    // 1. 创建PDF文档
    PdfWriter writer = new PdfWriter(response.getOutputStream());
    PdfDocument pdfDoc = new PdfDocument(writer);
    Document doc = new Document(pdfDoc);
    // 2. 添加标题
    Paragraph title = new Paragraph("用户清单").setFontSize(18).setBold();
    doc.add(title);
    doc.add(new Paragraph("n")); // 空行
    // 3. 创建表格
    Table table = new Table(UnitValue.createPercentArray(new float[]{20, 30, 20, 30}));
    table.addHeaderCell(createHeaderCell("ID"));
    table.addHeaderCell(createHeaderCell("姓名"));
    table.addHeaderCell(createHeaderCell("年龄"));
    table.addHeaderCell(createHeaderCell("注册时间"));
    // 4. 填充数据
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    for (User user : dataList) {
        table.addCell(new Cell().add(new Paragraph(String.valueOf(user.getId()))));
        table.addCell(new Cell().add(new Paragraph(user.getName())));
        table.addCell(new Cell().add(new Paragraph(String.valueOf(user.getAge()))));
        table.addCell(new Cell().add(new Paragraph(df.format(user.getRegisterTime()))));
    }
    // 5. 添加表格到文档
    doc.add(table);
    doc.close();
    // 6. 设置响应头
    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment;filename=users.pdf");
}
private Cell createHeaderCell(String text) {
    Cell cell = new Cell();
    cell.add(new Paragraph(text));
    cell.setBackgroundColor(DeviceCmyk.MAGENTA);
    cell.setTextAlignment(TextAlignment.CENTER);
    return cell;
}

排版技巧

  • 跨页表头重复:table.setHeaderRenderingMode(Table.HEADER_REPEATING);
  • 自动分页:doc.setAutoFlush(true);配合table.setKeepTogether(false)
  • 水印添加:使用Canvas绘制半透明文字/图片

通用处理逻辑(Controller层)

@GetMapping("/export")
public void exportData(@RequestParam String type, @RequestParam Long id, HttpServletResponse response) {
    List<User> dataList = userService.listByDeptId(id); // 根据业务逻辑获取数据
    String fileName = "data_" + System.currentTimeMillis() + "." + getFileExtension(type);
    try {
        switch (type.toLowerCase()) {
            case "excel":
                exportService.exportToExcel(response, dataList, fileName);
                break;
            case "csv":
                exportService.exportToCsv(response, dataList, fileName);
                break;
            case "pdf":
                exportService.exportToPdf(response, dataList, fileName);
                break;
            default:
                throw new IllegalArgumentException("不支持的导出类型");
        }
    } catch (Exception e) {
        throw new RuntimeException("导出失败", e);
    }
}

安全增强措施

  1. 文件名校验:fileName = FilenameUtils.getName(fileName).replaceAll("[^a-zA-Z0-9\u4e00-\u9fa5]", "");
  2. 防重复提交:使用@PreventDuplicateSubmit注解或Redis锁
  3. 权限控制:@PreAuthorize("hasPermission('data:export')")
  4. 超时机制:web.xml配置<error-page><error-code>504</error-code></error-page>应对长时间任务

常见问题与解决方案(FAQs)

Q1: 导出十万条数据时出现内存溢出怎么办?

A: 采用以下优化策略组合:

  1. 流式处理:改用SXSSFWorkbook(POI提供的低内存版本),设置WritableWorkbook的窗口大小(默认100行)
    SXSSFWorkbook wb = new SXSSFWorkbook(100); // 只保留100行在内存中
  2. 分块处理:将数据分成多个Sheet,每个Sheet存储1万条记录
  3. 延迟加载:使用CachingRowSetResultSet包装JDBC结果集,按需读取数据
  4. 垃圾回收:显式调用System.gc()并在适当位置添加null赋值,帮助JVM回收内存
  5. 异步导出:通过消息队列(RabbitMQ/Kafka)将导出任务放入后台处理,完成后通知用户下载链接

Q2: 导出的Excel文件打开提示“发现不可读取的内容”?

A: 这是微软Office的安全机制触发,解决方法如下:

  1. 禁用宏警告:在代码中添加以下元数据(针对.xlsm文件):
    PackagePart part = workbook.getPackagePart();
    PackageProperties prop = part.getProperties();
    prop.setCorrupted(false); // 标记为非损坏文件
  2. 修复ZIP压缩:确保生成的Excel文件是有效的ZIP压缩包,可用7-Zip验证完整性
  3. 更新依赖版本:升级POI至最新稳定版(当前推荐5.2.x系列)
  4. 另存为新文件:建议用户首次打开时选择“另存为”新文件,后续即可正常打开
  5. 移除无效属性:检查代码中是否错误设置了CustomPropertiesExtendedProperties,可能导致兼容性问题

性能对比测试(基于10万条数据)

指标 Excel (POI) CSV (OpenCSV) PDF (iText7)
生成时间 2s 1s 7s
文件大小 12MB 8MB 5MB
内存峰值 450MB 68MB 320MB
打开速度 中速 极快 较慢
二次编辑便利性
0