C#数据处理实战:用EPPlus把List<T>数据一键导出成美观的Excel文件
·
C#数据处理实战:用EPPlus把List 数据一键导出成美观的Excel文件
在后台开发中,数据导出是高频需求。想象这样的场景:你刚完成一个订单查询接口,前端同事问:"能不能加个导出Excel功能?"传统做法是手动遍历集合、设置单元格样式,不仅代码冗长,遇到日期格式化、多语言列名时更是头疼。EPPlus作为.NET平台最成熟的Excel操作库,能将这些繁琐过程封装成几行优雅代码。
1. 基础封装:从List 到Excel的智能转换
先通过NuGet安装EPPlus包:
Install-Package EPPlus
核心方法 ExportToExcel 接受任意 List<T> 并返回内存流,适合Web直接下载:
public static MemoryStream ExportToExcel<T>(List<T> data, string sheetName = "Sheet1")
{
using var package = new ExcelPackage();
var worksheet = package.Workbook.Worksheets.Add(sheetName);
// 属性名映射为中文列头
var properties = typeof(T).GetProperties();
for (int i = 0; i < properties.Length; i++)
{
var displayName = properties[i].GetCustomAttribute<DisplayNameAttribute>()?.DisplayName
?? properties[i].Name;
worksheet.Cells[1, i + 1].Value = displayName;
}
// 数据填充
if (data?.Count > 0)
{
worksheet.Cells["A2"].LoadFromCollection(data);
}
return new MemoryStream(package.GetAsByteArray());
}
典型应用场景 :
- 报表导出(订单、用户列表)
- 数据备份(数据库查询结果存档)
- 跨系统数据交换(符合Excel标准格式)
2. 样式优化:专业级表格视觉规范
基础导出只是开始,专业表格需要这些视觉元素:
2.1 智能样式配置
// 表头样式
using (var headerRange = worksheet.Cells[1, 1, 1, properties.Length])
{
headerRange.Style.Font.Bold = true;
headerRange.Style.Fill.PatternType = ExcelFillStyle.Solid;
headerRange.Style.Fill.BackgroundColor.SetColor(Color.LightGray);
headerRange.Style.Border.BorderAround(ExcelBorderStyle.Medium);
}
// 交替行颜色
var dataRange = worksheet.Cells[2, 1, data.Count + 1, properties.Length];
dataRange.Style.Fill.PatternType = ExcelFillStyle.Solid;
dataRange.Style.Fill.BackgroundColor.SetColor(Color.FromArgb(243, 243, 243));
2.2 自动列宽与数据类型识别
// 根据内容自动调整列宽
worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
// 特殊类型格式化
foreach (var prop in properties.Where(p => p.PropertyType == typeof(DateTime)))
{
int colIndex = Array.IndexOf(properties, prop) + 1;
worksheet.Column(colIndex).Style.Numberformat.Format = "yyyy-MM-dd";
}
实际效果对比 :
| 样式元素 | 未优化版本 | 优化后版本 |
|---|---|---|
| 表头 | 普通文本 | 加粗灰底 |
| 数据行 | 全白背景 | 交替灰白 |
| 日期字段 | 原始格式 | yyyy-MM-dd |
| 列宽 | 固定宽度 | 内容自适应 |
3. 高级技巧:应对复杂业务场景
3.1 动态列映射
通过 DisplayNameAttribute 实现中英文列名切换:
public class OrderDto
{
[DisplayName("订单编号")]
public string OrderId { get; set; }
[DisplayName("创建时间")]
public DateTime CreateTime { get; set; }
}
3.2 大数据量分块处理
避免内存溢出的分页写入方案:
int pageSize = 50000;
for (int page = 0; page < Math.Ceiling(data.Count / (double)pageSize); page++)
{
var chunk = data.Skip(page * pageSize).Take(pageSize);
worksheet.Cells[page * pageSize + 2, 1].LoadFromCollection(chunk);
// 每处理5万行保存一次
if (page % 10 == 0) package.Save();
}
3.3 自定义值转换器
处理枚举类型、空值等特殊情况:
public class StatusValueConverter : IValueConverter
{
public object Convert(object value)
{
return value switch {
OrderStatus.Pending => "待处理",
OrderStatus.Shipped => "已发货",
_ => value?.ToString()
};
}
}
// 使用转换器
worksheet.Cells[2, statusColIndex, data.Count + 1, statusColIndex]
.Style.Numberformat.Format = new StatusValueConverter();
4. 实战案例:电商订单导出系统
假设需要实现以下需求:
- 导出自定义时间段订单
- 包含商品明细子表
- 按状态自动标记颜色
核心代码结构 :
public async Task<IActionResult> ExportOrders(DateTime start, DateTime end)
{
var orders = await _orderRepo.GetByDateRangeAsync(start, end);
using var package = new ExcelPackage();
var mainSheet = package.Workbook.Worksheets.Add("订单汇总");
// 主表导出
mainSheet.Cells["A1"].LoadFromCollection(orders, true);
// 为不同状态设置颜色
foreach (var order in orders)
{
int row = orders.IndexOf(order) + 2;
var statusCell = mainSheet.Cells[row, statusCol];
statusCell.Style.Font.Color.SetColor(GetStatusColor(order.Status));
}
// 商品明细子表
var itemsSheet = package.Workbook.Worksheets.Add("商品明细");
itemsSheet.Cells["A1"].LoadFromCollection(
orders.SelectMany(o => o.Items),
true
);
return File(package.GetAsByteArray(),
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
$"订单_{DateTime.Now:yyyyMMdd}.xlsx");
}
提示:Web项目中使用MemoryStream时,务必配置Response的ContentType和文件名,否则可能下载到损坏文件
5. 性能调优与异常处理
5.1 内存管理最佳实践
- 使用
ExcelRange的Dispose释放资源 - 设置
ExcelPackage.Compression减少内存占用 - 禁用自动计算公式:
package.Workbook.CalcMode = ExcelCalcMode.Manual;
5.2 健壮性增强
try
{
// 导出操作
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "Excel导出失败");
throw new UserFriendlyException("生成Excel时发生错误");
}
finally
{
// 确保资源释放
package?.Dispose();
}
5.3 扩展性设计
通过策略模式支持多种导出格式:
public interface IDataExporter
{
MemoryStream Export<T>(List<T> data);
}
public class ExcelExporter : IDataExporter
{
public MemoryStream Export<T>(List<T> data)
{
// EPPlus实现
}
}
// 在DI容器注册
services.AddSingleton<IDataExporter, ExcelExporter>();
6. 现代化改进:.NET Core集成方案
对于ASP.NET Core项目,推荐这些增强实践:
6.1 依赖注入配置
services.AddScoped(provider =>
{
var settings = new ExcelPackageSettings {
Compression = CompressionLevel.Fastest
};
return new ExcelPackage(settings);
});
6.2 中间件封装
创建可复用的导出端点:
[HttpGet("export")]
public async Task<IActionResult> Export([FromQuery] ExportRequest request)
{
var data = await _service.GetExportDataAsync(request);
var stream = _exporter.Export(data);
return File(stream, "application/octet-stream", $"{request.Type}_{DateTime.Now:yyyyMMdd}.xlsx");
}
6.3 响应式前端集成
配合前端库实现进度显示:
axios.get('/api/orders/export', {
responseType: 'blob',
onDownloadProgress: progressEvent => {
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
console.log(`下载进度: ${percent}%`);
}
}).then(response => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'orders.xlsx');
document.body.appendChild(link);
link.click();
});
7. 单元测试策略
确保导出功能稳定的测试方案:
7.1 基础功能验证
[Fact]
public void Should_Generate_Valid_Excel_From_List()
{
// Arrange
var testData = new List<TestModel> { /* 测试数据 */ };
// Act
var stream = ExcelHelper.ExportToExcel(testData);
// Assert
using var package = new ExcelPackage(stream);
Assert.NotNull(package.Workbook);
Assert.Equal(1, package.Workbook.Worksheets.Count);
Assert.Equal(testData.Count, package.Workbook.Worksheets[0].Dimension.Rows - 1);
}
7.2 性能基准测试
[Benchmark]
public void Export_10000_Rows()
{
var largeData = Enumerable.Range(1, 10000)
.Select(i => new TestModel { /* 数据 */ })
.ToList();
ExcelHelper.ExportToExcel(largeData);
}
7.3 集成测试示例
[Fact]
public async Task Export_Endpoint_Should_Return_Excel_File()
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync("/api/export");
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
response.Content.Headers.ContentType.MediaType);
}
更多推荐



所有评论(0)