# NPOI 导出对象到 Excel 格式数据

## 简介

本文将介绍如何使用 aardio 结合 NPOI 库实现通用的 Excel 导出功能。

## 准备工作

本方案的核心思想是通过 NPOI 库实现通用 Excel 导出功能。在 aardio 中，我们只需导入 NPOI 扩展库即可开始使用。

## 代码实现

我们创建了一个通用的 Excel 导出工具类，主要包含两个核心功能模块：

1. **ExcelExport 类** - 包含基础导出逻辑
2. **数据转换桥接器** - 将 aardio 表对象转换为 NPOI Excel 对象

### 基础导出类 (ExcelExport)

```aardio
import NPOI;
import System.IO;
namespace ExcelExport {

    // 基础导出类
    class ExcelExport {
   
        // 导出方法
        export = function(exportData, fileName, sheetName = "Sheet1") {
            this.fields = {}; 

            this.fileName = fileName;
            this.sheetName = sheetName;

            // 创建新工作簿和工作表
            this.workbook = ..NPOI.XSSF.UserModel.XSSFWorkbook();
            this.sheet = this.workbook.CreateSheet(this.sheetName);

            // 设置表头样式
            var headerStyle = this.workbook.CreateCellStyle();
            var headerFont = this.workbook.CreateFont();
            headerFont.IsBold = true;
            headerStyle.SetFont(headerFont);

            // 写入数据
            this.writeData(exportData);

            // 创建表头行
            var header = this.sheet.CreateRow(0);
            for(i=1; #this.fields; 1) {
                var cell = header.CreateCell(i-1);
                cell.SetCellValue(this.fields[i]);
                cell.CellStyle = headerStyle;
                // 大数据量时自动列宽会影响性能
                // this.sheet.AutoSizeColumn(i-1);
            }

            // 创建内存流并返回
            var memoryStream = ..System.IO.MemoryStream();
            this.workbook.Write(memoryStream);
            return memoryStream;
        }

        // 抽象方法 - 由子类实现具体数据写入逻辑
        writeData = function(exportData) {
            error("请在子类中实现此方法");
        }
    }
}
```

### 数据转换桥接器

```aardio
namespace ExcelExport {

    // 数据转换桥接器
    class ExcelExportBridge {

        ctor() {
            this = ..ExcelExport.ExcelExport();
            this.fields = {}; 
        }

        // 实现数据写入方法
        writeData = function(exportData) {
            // 获取第一个对象的属性列表
            var firstItem = exportData[1];
            var properties = {};
            
            if(exportData.fields){
            	this.fields = exportData.fields;
            }	
            else{
            	for k,v in ..table.eachName(firstItem){
                	..table.push(this.fields,k); // 属性名转为标题格式	
            	}
            }
            
            // 创建数据行
            for(i=1; #exportData; 1) {
                var row = this.sheet.CreateRow(i);
                var item = exportData[i];
                 
                for(j=1; #this.fields; 1) {
                    var cell = row.CreateCell(j-1);
                    var value = item[this.fields[j]];
                  	cell.SetCellValue(tostring(value));
                }
            }
        }
    }
}
```

## 使用示例

```aardio

// 准备测试数据
var testData = [
	fields:["Name","Age","Salary","JoinDate"],//指定列
    { Name = "张三", Age = 25, Salary = 5000.50, JoinDate = time() },
    { Name = "李四", Age = 30, Salary = 6000.75, JoinDate = time() },
    { Name = "王五", Age = 28, Salary = 5500.25, JoinDate = time() }
];

// 创建导出器实例
var exporter = ExcelExport.ExcelExportBridge();

// 导出数据到内存流
var stream = exporter.export(testData, "员工数据");

// 将流保存为文件
System.IO.File.WriteAllBytes("/员工数据.xlsx", stream.ToArray());

// 或者直接返回给 HTTP 响应（Web 应用场景）
/*
response.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
response.headers["Content-Disposition"] = 'attachment; filename="员工数据_'++time.format(time(),"yyyyMMddHHmmss")++'.xlsx"';
response.write( stream.ToArray() )
*/
```

## 技术背景

我们使用 NPOI 库来实现 Excel 导出功能，这是一个免费开源的 .NET 库。相比传统的字符串拼接或 Interop 方式，NPOI 提供了更高效、更稳定的 Excel 操作能力。

## 实现要点

1. **通用性**：可以处理任意类型的对象列表
2. **灵活性**：自动识别属性名作为列标题
3. **类型安全**：根据属性类型正确处理单元格格式
4. **性能优化**：避免大数据量时的自动列宽计算

## 改进方向

1. 移除对 DataTable 的依赖（aardio 中直接使用表对象）
2. 增加更多数据类型支持
3. 优化大文件导出性能
4. 添加样式自定义选项

## 实际应用

当我们需要为多个数据表提供导出功能时，这种通用方案可以大幅减少重复代码。相比为每个表单独实现导出逻辑，此方案只需准备数据并调用统一接口即可。

## 注意事项

1. 大数据量导出时建议分页处理
2. 复杂格式需求可能需要扩展样式设置
3. 日期时间格式可根据需求调整

这个方案已在生产环境中验证，能够稳定处理各种数据导出需求。您可以根据实际项目需要进行进一步定制和优化。