65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 NPOI – C# 和 WEB API 导出到 Excel

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (8投票s)

2018年4月28日

CPOL

3分钟阅读

viewsIcon

104763

downloadIcon

1210

使用 NPOI 和 .NET/.NET Core WEB API 以通用方式将 List 导出到 Excel 文件

引言

本文将引导您了解使用 C# 在 WEB API 中通过 NPOI 实现的通用导出到 Excel 功能,该功能经过测试且运行良好。

本文特定于 .NET Framework,但我已在 .NET Core 中进行了尝试,它可以完美运行。 因此请稍候,让我们开始吧。

在我们开始之前

本文的主要概念是通过导入 NPOI 并添加示例中包含的以下两个 cs 文件来开发通用的 Excel 导出功能,然后就可以开始了。

探索代码

我为您创建了一个 GitHub 存储库,您可以在其中使用 ASP.NET MVC 探索代码,就在这里

背景

我们使用 NPOI DLL 进行此导出,该 DLL 可免费使用,有关更多详细信息,请参阅 NPOI NuGet

我们经常需要在我们的应用程序中开发导出到 Excel 的功能,我们中的许多人通常会创建无聊的 string 构建器,然后将其转换为 Excel 或使用 Interop 或 ITextSharp 或 NPOI 或其他一些方法来实现相同的结果。

上面列出的所有方法在它们自己的上下文中绝对正确且良好,但是如果有一种方法可以像传递一个对象并获得所需的输出一样简单地导出到 Excel,我们的生活是否会更容易?

这就是我将在本文中解释的内容。

Using the Code

首先,有一个名为 Export() 的实用函数,它只是将您的 C# List 对象转换为 NPOI 对象,然后最终提供给 HttpResponseMessage 类型,可以在您的 WEB API Action 中使用。

您需要 2 个文件才能实现它 - 参阅本文中附带的解决方案以更好地理解 >> ExcelExport 文件夹在 Root 目录中

  1. AbstractDataExport.cs - 包含通用代码
  2. AbstractDataExportBridge.cs - 将 List 转换为 NPOI Excel 对象

AbstractDataExport.cs 做什么?

参考 Export(List exportData, string fileName, string sheetName = DefaultSheetName)

让我们从第一个文件,AbstractDataExport.cs 开始

创建新的 Excel 对象 - _workbook = new XSSFWorkbook();

  1. 创建新的 Excel Sheet 对象 - _sheet = _workbook.CreateSheet(_sheetName);
  2. 调用 WriteData() - 稍后解释
  3. 最后,创建并返回 MemoryStream 对象
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;

namespace GenericExcelExport.ExcelExport
{
    public interface IAbstractDataExport
    {
        HttpResponseMessage Export(List exportData, string fileName, string sheetName);
    }

    public abstract class AbstractDataExport : IAbstractDataExport
    {
        protected string _sheetName;
        protected string _fileName;
        protected List _headers;
        protected List _type;
        protected IWorkbook _workbook;
        protected ISheet _sheet;
        private const string DefaultSheetName = "Sheet1";

        public HttpResponseMessage Export
              (List exportData, string fileName, string sheetName = DefaultSheetName)
        {
            _fileName = fileName;
            _sheetName = sheetName;

            _workbook = new XSSFWorkbook(); //Creating New Excel object
            _sheet = _workbook.CreateSheet(_sheetName); //Creating New Excel Sheet object

            var headerStyle = _workbook.CreateCellStyle(); //Formatting
            var headerFont = _workbook.CreateFont();
            headerFont.IsBold = true;
            headerStyle.SetFont(headerFont);

            WriteData(exportData); //your list object to NPOI excel conversion happens here

            //Header
            var header = _sheet.CreateRow(0);
            for (var i = 0; i < _headers.Count; i++)
            {
                var cell = header.CreateCell(i);
                cell.SetCellValue(_headers[i]);
                cell.CellStyle = headerStyle;
                // It's heavy, it slows down your Excel if you have large data                
                //_sheet.AutoSizeColumn(i);
            }

            using (var memoryStream = new MemoryStream()) //creating memoryStream
            {
                _workbook.Write(memoryStream);
                var response = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new ByteArrayContent(memoryStream.ToArray())
                };

                response.Content.Headers.ContentType = new MediaTypeHeaderValue
                       ("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.Content.Headers.ContentDisposition = 
                       new ContentDispositionHeaderValue("attachment")
                {
                    FileName = $"{_fileName}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx"
                };

                return response;
            }
        }

        //Generic Definition to handle all types of List
        public abstract void WriteData(List exportData);
    }
}

现在,让我们继续我们的第二个也是最后一个文件,即 AbstractDataExportBridge.cs。 以下是 WriteData(List exportData) 的说明

  1. List 转换为 DataTable
  2. 反射 读取属性名称,您的列标题将从这里获取
  3. 循环遍历 DataTable 以创建 Excel 行

这里有可以改进的地方,您可以进行必要的更改,例如完全删除 DataTable。

using NPOI.SS.UserModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text.RegularExpressions;

namespace GenericExcelExport.ExcelExport
{
    public class AbstractDataExportBridge : AbstractDataExport
    {
        public AbstractDataExportBridge()
        {
            _headers = new List<string>();
            _type = new List<string>();
        }

        public override void WriteData<T>(List<T> exportData)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));

            DataTable table = new DataTable();

            foreach (PropertyDescriptor prop in properties)
            {
                var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                _type.Add(type.Name);
                table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? 
                                  prop.PropertyType);
                string name = Regex.Replace(prop.Name, "([A-Z])", " $1").Trim();
                //name by caps for header
                _headers.Add(name);
            }

            foreach (T item in exportData)
            {
                DataRow row = table.NewRow();
                foreach (PropertyDescriptor prop in properties)
                    row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                table.Rows.Add(row);
            }

            IRow sheetRow = null;

            for (int i = 0; i < table.Rows.Count; i++)
            {
                sheetRow = _sheet.CreateRow(i + 1);

                for (int j = 0; j < table.Columns.Count; j++)
                {
                    ICell Row1 = sheetRow.CreateCell(j);
                    string cellvalue = Convert.ToString(table.Rows[i][j]);
                    
                    // TODO: move it to switch case

                    if (string.IsNullOrWhiteSpace(cellvalue))
                    {
                        Row1.SetCellValue(string.Empty);
                    }
                    else if (_type[j].ToLower() == "string")
                    {
                        Row1.SetCellValue(cellvalue);
                    }
                    else if (_type[j].ToLower() == "int32")
                    {
                        Row1.SetCellValue(Convert.ToInt32(table.Rows[i][j]));
                    }
                    else if (_type[j].ToLower() == "double")
                    {
                        Row1.SetCellValue(Convert.ToDouble(table.Rows[i][j]));
                    }
                    else if (_type[j].ToLower() == "datetime")
                    {
                        Row1.SetCellValue(Convert.ToDateTime
                             (table.Rows[i][j]).ToString("dd/MM/yyyy hh:mm:ss"));
                    }
                    else
                    {
                        Row1.SetCellValue(string.Empty);
                    }
                }
            }
        }
    }
}

关注点

当我需要提供 Excel 导出功能的表单超过 20 个时,我发现了这个解决方案,而且我不愿意使用在我这种情况下会很冗长的传统方法。

所有事情总是有改进的余地。 如果您在这里看到任何需要改进的地方,请在评论中更新。

历史

  • 2018 年 4 月 27 日:初稿
  • 2019 年 8 月 12 日:根据我的应用程序中的新发现,进行了性能改进
© . All rights reserved.