ASP.Net MVC利用NPOI导入导出Excel示例代码|完整教程附可直接运行的代码

文章目录CloseOpen

    • 为什么选NPOI?先把底层逻辑讲明白
    • 实操:从0到1实现导入导出,代码直接抄
      • 第一步:装依赖,别踩预发布版的坑
      • 第二步:封装通用工具类,避免重复造轮子
      • 第三步:导出实现,别让表格丑到没法看
      • 第四步:前端调用,别忘设置响应头
    • 最后说个避坑小技巧:表头验证
      • NPOI支持哪些Excel版本?
      • 使用NPOI需要在服务器上安装Office吗?
      • NPOI导出Excel时如何设置单元格样式?
      • 导入Excel时遇到空单元格会报错吗?
      • NPOI如何区分处理.xls和.xlsx文件?

    为什么选NPOI?先把底层逻辑讲明白

    先问个扎心的问题:你之前用Office Interop做服务器端Excel处理时,有没有遇到过“内存泄漏”或者“权限不足”的问题?我之前帮客户做会员数据导出时,就因为服务器没装Office,导致每天凌晨导出任务都失败,排查了三天才发现是COM组件的锅。而NPOI刚好解决了这个痛点——它是Apache POI的.NET版本,直接操作Excel的二进制文件格式(比如.xls是BIFF8格式,.xlsx是OOXML),完全不用依赖Office软件。

    谷歌开发者博客里其实早就说过,服务器端处理Office文件最安全的方式就是用这种“无依赖”库,因为COM组件不仅容易内存泄漏,还得配置复杂的权限(比如给IIS用户赋Office的访问权限)。我换成NPOI后,服务器再也没出现过“内存不足”的报警,运维小哥都夸我优化得好。

    再给你对比下常见的Excel处理工具,你就明白NPOI的优势了:

    工具 核心优势 依赖要求 稳定性 学习成本
    NPOI 无Office依赖、支持多版本Excel、轻量 仅需.NET框架 高(无内存泄漏) 中(文档全、示例多)
    Office Interop 完全模拟Office操作 必须安装对应版本Office 低(易内存泄漏) 高(需学COM组件)
    ClosedXML API友好、支持.xlsx .NET 4.5+ 中(复杂样式支持差) 低(语法简洁)

    简单说,如果你的项目需要跨版本支持(比如同时处理Excel 2003和2016),或者部署到无Office的服务器,NPOI绝对是首选。我那个电商客户的订单导出要支持老会计用的Excel 2003,NPOI的HSSF(.xls)和XSSF(.xlsx)双类库刚好覆盖,改个后缀名就能切换,特别省心。

    实操:从0到1实现导入导出,代码直接抄

    说了这么多理论,直接上硬菜——我把项目里用的Excel工具类和核心代码拆出来,你跟着改改就能用。

    第一步:装依赖,别踩预发布版的坑

    首先打开Visual Studio的NuGet包管理器,搜索“NPOI”,选最新的稳定版安装(比如当前的2.6.0)——别选预发布版!我之前贪新鲜试了预发布版,结果导出.xlsx时单元格样式全丢了,折腾了半天才换回稳定版。安装完成后,项目里会自动引用NPOI.dll、NPOI.OOXML.dll这些文件,不用管,直接写代码就行。

    第二步:封装通用工具类,避免重复造轮子

    为什么要封装?因为导入导出的核心逻辑就那几步,封装成工具类能省80%的重复代码。我一般会写一个ExcelHelper静态类,里面放两个核心方法:ImportExcel(导入成实体列表)和ExportExcel(导出实体列表)。

    先看导入方法的逻辑——比如导入用户信息(实体类UserIdNameAgeRegisterDate四个属性):

  • 读文件流:根据文件后缀(.xls/.xlsx)创建对应的Workbook(HSSF对应.xls,XSSF对应.xlsx);
  • 取Sheet:默认取第一个Sheet(你也可以加参数指定Sheet名称);
  • 跳表头:遍历行时从第1行开始(第0行是表头);
  • 映射实体:把每行单元格的值对应到实体属性,注意单元格类型匹配(比如日期用GetDateCellValue(),文本用StringCellValue)。
  • 直接上代码框架:

public static class ExcelHelper

{

public static List ImportExcel(Stream fileStream, string suffix) where T new()

{

var result = new List();

IWorkbook workbook = null;

// 根据后缀创建Workbook

if (suffix.Equals(".xls", StringComparison.OrdinalIgnoreCase))

{

workbook = new HSSFWorkbook(fileStream);

}

else if (suffix.Equals(".xlsx", StringComparison.OrdinalIgnoreCase))

{

workbook = new XSSFWorkbook(fileStream);

}

else

{

throw new ArgumentException("不支持的文件格式");

}

var sheet = workbook.GetSheetAt(0); // 取第一个Sheet

if (sheet == null) return result;

var properties = typeof(T).GetProperties(); // 获取实体属性

// 遍历行(跳过表头,从第1行开始)

for (int i = 1; i <= sheet.LastRowNum; i++)

{

var row = sheet.GetRow(i);

if (row == null) continue;

var entity = new T();

for (int j = 0; j < properties.Length; j++)

{

var cell = row.GetCell(j, MissingCellPolicy.CREATE_NULL_AS_BLANK); // 空单元格处理

var property = properties[j];

// 根据属性类型赋值

switch (property.PropertyType.Name)

{

case "String":

property.SetValue(entity, cell.StringCellValue);

break;

case "Int32":

if (cell.CellType == CellType.Numeric)

{

property.SetValue(entity, (int)cell.NumericCellValue);

}

break;

case "DateTime":

if (cell.CellType == CellType.Numeric)

{

property.SetValue(entity, cell.GetDateCellValue());

}

break;

// 其他类型可以自己扩展

}

}

result.Add(entity);

}

return result;

}

}

这里有个关键细节MissingCellPolicy.CREATE_NULL_AS_BLANK——如果单元格为空,会创建一个空单元格,避免空引用报错。我之前导入客户信息时,没加这个参数,结果遇到空单元格直接崩了,后来加了才解决。

第三步:导出实现,别让表格丑到没法看

导出比导入简单,但要注意样式优化——不然导出的表格像“毛坯房”,用户根本不想用。我一般会给导出加这些样式:

  • 表头:蓝色背景、加粗、居中;
  • 内容行:交替颜色(比如奇数行浅灰,偶数行白);
  • 列宽:根据内容调整(比如日期列设为15字符宽);
  • 数据对齐:数字右对齐、文本左对齐、日期居中。
  • 直接上导出方法的核心代码:

    public static MemoryStream ExportExcel(List data, string sheetName = "Sheet1")
    

    {

    IWorkbook workbook = new XSSFWorkbook(); // 默认导出.xlsx,要导.xls换HSSFWorkbook

    var sheet = workbook.CreateSheet(sheetName);

    var properties = typeof(T).GetProperties();

    int rowIndex = 0;

    //

  • 创建表头
  • var headerRow = sheet.CreateRow(rowIndex++);

    var headerStyle = CreateHeaderStyle(workbook); // 表头样式

    for (int i = 0; i < properties.Length; i++)

    {

    var cell = headerRow.CreateCell(i);

    cell.SetCellValue(properties[i].Name);

    cell.CellStyle = headerStyle;

    sheet.SetColumnWidth(i, 20 256); // 列宽:20字符(256是单位)

    }

    //

  • 填充内容
  • var contentStyle = CreateContentStyle(workbook); // 内容样式

    for (int i = 0; i < data.Count; i++)

    {

    var row = sheet.CreateRow(rowIndex++);

    var item = data[i];

    // 交替行颜色

    if (i % 2 == 1)

    {

    row.RowStyle = CreateAlternateRowStyle(workbook);

    }

    for (int j = 0; j < properties.Length; j++)

    {

    var cell = row.CreateCell(j);

    var value = properties[j].GetValue(item);

    // 根据类型设置值和对齐

    if (value is DateTime date)

    {

    cell.SetCellValue(date);

    cell.CellStyle = CreateDateStyle(workbook); // 日期样式(居中、yyyy-MM-dd)

    }

    else if (value is int || value is double)

    {

    cell.SetCellValue(Convert.ToDouble(value));

    cell.CellStyle = CreateNumericStyle(workbook); // 数字样式(右对齐)

    }

    else

    {

    cell.SetCellValue(value?.ToString() ?? "");

    cell.CellStyle = CreateTextStyle(workbook); // 文本样式(左对齐)

    }

    }

    }

    //

  • 写入流
  • var stream = new MemoryStream();

    workbook.Write(stream);

    stream.Seek(0, SeekOrigin.Begin);

    return stream;

    }

    // 辅助方法:创建表头样式

    private static ICellStyle CreateHeaderStyle(IWorkbook workbook)

    {

    var style = workbook.CreateCellStyle();

    style.FillForegroundColor = IndexedColors.LightBlue.Index;

    style.FillPattern = FillPattern.SolidForeground;

    var font = workbook.CreateFont();

    font.Boldweight = (short)FontBoldWeight.Bold;

    style.SetFont(font);

    style.Alignment = HorizontalAlignment.Center;

    return style;

    }

    这里要注意:

  • 导出.xls用HSSFWorkbook,导出.xlsx用XSSFWorkbook,别搞反了——我之前导.xlsx用了HSSF,结果用户打开说“文件格式无效”,换成XSSF就好了;
  • 列宽的单位是“256分之一字符”,所以20 256就是20字符宽,刚好放下日期或姓名;
  • 交替行颜色能让表格更易读,老会计都夸这个设计贴心。
  • 第四步:前端调用,别忘设置响应头

    最后一步是把导出的流返回给前端下载。在ASP.Net MVC的Controller里,你可以这么写:

    public ActionResult ExportUsers()
    

    {

    var users = _userService.GetAllUsers(); // 从数据库取数据

    var stream = ExcelHelper.ExportExcel(users, "用户列表");

    // 关键:设置响应头,告诉浏览器这是Excel文件

    Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

    Response.AddHeader("Content-Disposition", "attachment; filename=用户列表.xlsx");

    return new FileStreamResult(stream, Response.ContentType);

    }

    别漏了Content-Disposition头——不然浏览器会把Excel文件当成网页打开,我第一次做的时候就犯过这错,用户反馈“下载的文件打不开”,后来加了头才正常。

    最后说个避坑小技巧:表头验证

    导入时一定要加表头验证——比如要求表头必须是“Id,Name,Age,RegisterDate”,不然直接返回错误。我一般会在ImportExcel方法里加这段代码:

    // 验证表头
    

    var headerRow = sheet.GetRow(0);

    var expectedHeaders = new List { "Id", "Name", "Age", "RegisterDate" };

    for (int i = 0; i < expectedHeaders.Count; i++)

    {

    if (headerRow.GetCell(i).StringCellValue != expectedHeaders[i])

    {

    throw new Exception($"表头格式错误,第{i+1}列应为{expectedHeaders[i]}");

    }

    }

    这样能避免用户传错Excel文件(比如把“年龄”写成“Age1”),减少后续的错误数据处理。我之前没加这个验证,结果导入了一堆格式错误的数据,花了半天时间才清理干净。

    好了,以上就是我用NPOI做Excel导入导出的全部经验——代码都是项目里跑过的,你直接抄过去改改实体类就能用。如果遇到问题,比如单元格类型判断不对,或者样式不生效,欢迎留言告诉我,我帮你看看——毕竟我踩过的坑,能让你少走点弯路。对了,导出的时候记得测试不同版本的Excel,比如用Excel 2003和2021都打开试试,确保兼容性。试试吧,搞定了记得回来报个喜!


    其实NPOI里设置单元格样式真的没想象中复杂,核心就是先搞个ICellStyle对象,然后把你想要的样式“堆”进去就行——我平时做项目的时候,光表头样式就有固定套路:先选个浅蓝背景,用IndexedColors.LightBlue这枚举值,再把FillPattern设成SolidForeground(不然背景色显示不出来);然后字体要加粗,得先创建Font对象,把Boldweight改成Bold,再绑到CellStyle上;最后对齐方式设成居中,这样表头一眼看过去清清楚楚,用户打开Excel第一反应就是“这表格规整”。

    内容行的样式我更在意实用性,比如奇偶行交替颜色——奇数行用浅灰色,偶数行留白色,这样读数据的时候不容易串行,实现起来也简单:循环内容行的时候,判断行号是不是奇数,是的话就给RowStyle赋个浅灰的CellStyle。还有数据对齐,像金额、年龄这种数字,右对齐看着更舒服;日期比如注册时间,肯定要居中,还得把格式改成yyyy-MM-dd,这时候得单独写个CreateDateStyle方法,把单元格格式设好再绑上去。对了,不管你是导.xls还是.xlsx,样式逻辑都一样,但Workbook得对应好——导.xlsHSSFWorkbook,导.xlsxXSSFWorkbook,我之前就犯过傻,导.xlsx的时候用了HSSF,结果导出的表头没颜色,折腾半天才反应过来是Workbook搞混了。

    还有字体大小,你要是想让表头更突出,可以把FontFontHeightInPoints设成12号,内容行用10号,层次一下子就出来了;边框的话我很少加全框,太密显得乱,一般就给表头加个下边框,用BorderStyle.Thin细实线,把表头和内容分隔开就行。我之前做电商订单导出的时候,把金额列设成右对齐,日期列居中用yyyy-MM-dd格式,表头用深蓝加粗,用户反馈说“比之前的导出表格好用10倍”,还问我是不是专门学了Excel美化——其实就是用对了NPOI的样式设置而已。


    NPOI支持哪些Excel版本?

    NPOI支持Excel 2003及以下版本(.xls格式,对应HSSF类库)和Excel 2007及以上版本(.xlsx格式,对应XSSF类库),可通过判断文件后缀切换处理方式,覆盖大多数用户的Excel使用场景。

    使用NPOI需要在服务器上安装Office吗?

    不需要。NPOI直接操作Excel的二进制文件格式(如.xls的BIFF8格式、.xlsx的OOXML格式),不依赖任何Office组件,解决了服务器端因未安装Office导致的兼容性问题。

    NPOI导出Excel时如何设置单元格样式?

    可通过创建ICellStyle对象实现样式自定义:比如表头样式可设置蓝色背景、加粗字体和居中对齐;内容行可设置交替颜色或数据对齐(如数字右对齐、日期居中)。需注意不同Excel版本(.xls/.xlsx)的样式创建逻辑一致,但Workbook实例需对应(HSSF对应.xls,XSSF对应.xlsx)。

    导入Excel时遇到空单元格会报错吗?

    默认情况下可能会因空引用报错,需在获取单元格时添加MissingCellPolicy.CREATE_NULL_AS_BLANK参数,让空单元格自动创建空值单元格,避免空引用异常。例如row.GetCell(j, MissingCellPolicy.CREATE_NULL_AS_BLANK)。

    NPOI如何区分处理.xls和.xlsx文件?

    通过文件后缀判断:若后缀为.xls,使用HSSFWorkbook类创建Workbook实例;若为.xlsx,使用XSSFWorkbook类。处理逻辑一致,仅需根据后缀切换Workbook类型即可实现跨版本兼容。

    温馨提示:本站提供的一切软件、教程和内容信息都来自网络收集整理,仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负,版权争议与本站无关。用户必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。如果您喜欢该程序和内容,请支持正版,购买注册,得到更好的正版服务。我们非常重视版权问题,如有侵权请邮件与我们联系处理。敬请谅解! 联系邮箱:lgg.sinyi@qq.com

    给TA打赏
    共{{data.count}}人
    人已打赏
    行业资讯

    通达信AI智能量化选股指标源码免费分享|实战可用的完整量化选股代码

    2025-9-10 17:14:20

    行业资讯

    多商户商城源码哪个好|推荐好用靠谱的多商户系统源码

    2025-9-10 17:46:28

    0 条回复 A文章作者 M管理员
      暂无讨论,说说你的看法吧
    个人中心
    购物车
    优惠劵
    今日签到
    有新私信 私信列表
    搜索