View Javadoc
1   package edu.jiangxin.apktoolbox.utils;
2   
3   import org.apache.logging.log4j.LogManager;
4   import org.apache.logging.log4j.Logger;
5   import org.apache.poi.xssf.usermodel.XSSFWorkbook;
6   import org.apache.poi.ss.usermodel.Row;
7   import org.apache.poi.ss.usermodel.Sheet;
8   
9   import javax.swing.*;
10  import javax.swing.filechooser.FileNameExtensionFilter;
11  import javax.swing.table.TableModel;
12  import java.awt.*;
13  import java.io.File;
14  import java.io.FileOutputStream;
15  import java.io.IOException;
16  
17  /**
18   * 工具类:将 JTable 的 TableModel 数据导出为 .xlsx 文件。
19   */
20  public class ExcelExporter {
21  
22      private static final Logger logger = LogManager.getLogger(ExcelExporter.class);
23  
24      private ExcelExporter() {
25          // 工具类,禁止实例化
26      }
27  
28      /**
29       * 完整导出流程:空数据检查 → 文件选择 → 写文件 → 结果提示。
30       * 必须在 EDT 中调用。
31       *
32       * @param tableModel      数据来源
33       * @param defaultFileName 文件保存对话框的默认文件名,例如 "pdf_stat_export.xlsx"
34       * @param parent          父组件,用于对话框定位
35       */
36      public static void export(TableModel tableModel, String defaultFileName, Component parent) {
37          if (tableModel.getRowCount() == 0) {
38              SwingUtilities.invokeLater(() ->
39                  JOptionPane.showMessageDialog(parent, "当前没有可导出的数据", "提示", JOptionPane.INFORMATION_MESSAGE));
40              return;
41          }
42  
43          JFileChooser fileChooser = new JFileChooser();
44          fileChooser.setDialogTitle("保存 Excel 文件");
45          fileChooser.setFileFilter(new FileNameExtensionFilter("Excel 文件 (*.xlsx)", "xlsx"));
46          fileChooser.setSelectedFile(new File(defaultFileName));
47  
48          int result = fileChooser.showSaveDialog(parent);
49          if (result != JFileChooser.APPROVE_OPTION) {
50              return;
51          }
52  
53          File selectedFile = fileChooser.getSelectedFile();
54          // 自动追加 .xlsx 后缀
55          if (!selectedFile.getName().toLowerCase().endsWith(".xlsx")) {
56              selectedFile = new File(selectedFile.getAbsolutePath() + ".xlsx");
57          }
58          final File targetFile = selectedFile;
59  
60          // 在后台线程写文件,避免阻塞 EDT
61          final TableModel modelSnapshot = tableModel;
62          new Thread(() -> {
63              try {
64                  writeToFile(modelSnapshot, targetFile);
65                  final String absolutePath = targetFile.getAbsolutePath();
66                  SwingUtilities.invokeLater(() ->
67                      JOptionPane.showMessageDialog(parent, "导出成功:" + absolutePath, "成功", JOptionPane.INFORMATION_MESSAGE));
68              } catch (IOException e) {
69                  logger.error("导出 Excel 失败", e);
70                  final String errorMsg = e.getMessage();
71                  SwingUtilities.invokeLater(() ->
72                      JOptionPane.showMessageDialog(parent, "导出失败:" + errorMsg, "错误", JOptionPane.ERROR_MESSAGE));
73              }
74          }).start();
75      }
76  
77      /**
78       * 将 TableModel 数据写入指定文件(在后台线程调用)。
79       */
80      static void writeToFile(TableModel tableModel, File targetFile) throws IOException {
81          try (XSSFWorkbook workbook = new XSSFWorkbook()) {
82              Sheet sheet = workbook.createSheet("Sheet1");
83  
84              // 写表头
85              Row headerRow = sheet.createRow(0);
86              for (int col = 0; col < tableModel.getColumnCount(); col++) {
87                  headerRow.createCell(col).setCellValue(tableModel.getColumnName(col));
88              }
89  
90              // 写数据行
91              for (int row = 0; row < tableModel.getRowCount(); row++) {
92                  Row dataRow = sheet.createRow(row + 1);
93                  for (int col = 0; col < tableModel.getColumnCount(); col++) {
94                      Object value = tableModel.getValueAt(row, col);
95                      dataRow.createCell(col).setCellValue(value == null ? "" : value.toString());
96                  }
97              }
98  
99              try (FileOutputStream fos = new FileOutputStream(targetFile)) {
100                 workbook.write(fos);
101             }
102         }
103     }
104 }