.Net core NPOI导入导出Excel

扶醉桌前 提交于 2019-12-30 18:21:07

     最近在想.net core NPOI 导入导出Excel,一开始感觉挺简单的,后来真的遇到很多坑。所以还是写一篇博客让其他人少走一些弯路,也方便忘记了再重温一遍。好了,多的不说,直接开始吧。

在.Net core 使用NPOI首先必须先安装DotNetCore.NPOI,第一种方法可以在管理Nuget包中安装,如图:

直接搜索DotNet.NPOI 安装就可以了。

第二种方法就是在Nuget程序包控制台中输入 Install-Package DotNetCore.NPOI  然后回车就可以了。

首先导出,和网上很多例子一样,我的想法一开始是用datatable导出到Excel中,但是感觉datatable的性能不够好,而且局限比较大,不过一开始我还是用datatable导出来了。

代码如下:

 

public static byte[] Output(DataTable dataTable, string[] tableTitle)
        {
            NPOI.SS.UserModel.IWorkbook workbook = new NPOI.XSSF.UserModel.XSSFWorkbook();
            NPOI.SS.UserModel.ISheet sheet = workbook.CreateSheet("sheet");
            IRow Title = null;
            IRow rows = null;
            for (int i = 1; i <= dataTable.Rows.Count; i++)
            {
                //创建表头
                if (i - 1 == 0)
                {
                    Title = sheet.CreateRow(0);
                    for (int k = 1; k < tableTitle.Length + 1; k++)
                    {
                        Title.CreateCell(0).SetCellValue("序号");
                        Title.CreateCell(k).SetCellValue(tableTitle[k - 1]);
                    }
                    continue;
                }
                else
                {
                    rows = sheet.CreateRow(i - 1);
                    for (int j = 1; j <= dataTable.Columns.Count; j++)
                    {
                        rows.CreateCell(0).SetCellValue(i - 1);
                        rows.CreateCell(j).SetCellValue(dataTable.Rows[i - 1][j - 1].ToString());
                    }
                }
            }

            byte[] buffer = new byte[1024 * 5];
            using (MemoryStream ms = new MemoryStream())
            {
                workbook.Write(ms);
                buffer = ms.GetBuffer();
                ms.Close();
            }
            return buffer;
        }

 这里返回的是byte数组是为了方便在控制器调用方法后中返回一个文件给客户端。

return File(buffer, "application/ms-excel", "list.xlsx");   

这样客户端直接会有一个文件下载。

当然别忘了引用

using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;

这里XSSF是适用于xlsx,如果是xls的话引用using NPOI.HSSF.UserModel;

可以发现这里的数据源是datatable类型的,对于使用EF Core 操作数据库不是特别方便,所以还是改用list泛型去导出会方便很多。

代码如下:

public static byte[] OutputExcel(List<T> entitys, string[] title)
        {
            IWorkbook workbook = new XSSFWorkbook();
            ISheet sheet = workbook.CreateSheet("sheet");
            IRow Title = null;
            IRow rows = null;
            Type entityType = entitys[0].GetType();
            PropertyInfo[] entityProperties = entityType.GetProperties();

            for (int i = 0; i <= entitys.Count; i++)
            {
                if (i == 0)
                {
                    Title = sheet.CreateRow(0);
                    for (int k = 1; k < title.Length + 1; k++)
                    {
                        Title.CreateCell(0).SetCellValue("序号");
                        Title.CreateCell(k).SetCellValue(title[k - 1]);
                    }

                    continue;
                }
                else
                {
                    rows = sheet.CreateRow(i);
                    object entity = entitys[i - 1];
                    for (int j = 1; j <= entityProperties.Length; j++)
                    {
                        object[] entityValues = new object[entityProperties.Length];
                        entityValues[j - 1] = entityProperties[j - 1].GetValue(entity);
                        rows.CreateCell(0).SetCellValue(i);
                        rows.CreateCell(j).SetCellValue(entityValues[j - 1].ToString());
                    }
                }
            }

            byte[] buffer = new byte[1024 * 2];
            using (MemoryStream ms = new MemoryStream())
            {
                workbook.Write(ms);
                buffer = ms.GetBuffer();
                ms.Close();
            }

            return buffer;
        }

因为list<T>可以直接得出行数,所以得出行数很方便,但是不知道list<T>有多少列,这个时候需要用到反射,记得引用System.Reflection 。通过获取list<T>类型去获取list<T>的属性。

然后通过 entityProperties.Length 获取列数。当然这里要留意第一列是表头,第二列才是数据,导出的时候从第二列开始。

通用引用类型去获取一行的值object entity = entitys[i - 1];然后通过属性数组去获取每一列的值,这里的逻辑有点乱,细心一点就一目了然了。object[] entityValues = new object[entityProperties.Length]; entityValues[j - 1] = entityProperties[j - 1].GetValue(entity);

这里是先写到内存中再导出,可能数据多了的时候会慢,不过一般是不会的。导出在这里就结束了。

 

然后是导入:

还是因为EF Core的原因,感觉还是导入成List<T>最方便,因为是公共方法,所以根据实体类型不同,要兼容所有实体类型,泛型似乎是最好的选择。

在网上找了很多资料,也看到了很多方法实现,不过不是特别适合我现在所在的项目,所以还是自己结合网上的重写,忘了借鉴了的文章的作者,若有侵权,马上删除。

代码如下:

 

 /// <summary>
        /// 导入Excel
        /// </summary>
        /// <param name="file">导入文件</param>
        /// <returns>List<T></returns>
        public static List<T> InputExcel(IFormFile file)
        {
            List<T> list = new List<T> { };

            MemoryStream ms = new MemoryStream();
            file.CopyTo(ms);
            ms.Seek(0, SeekOrigin.Begin);

            IWorkbook workbook = new XSSFWorkbook(ms);
            ISheet sheet = workbook.GetSheetAt(0);
            IRow cellNum = sheet.GetRow(0);
            var propertys = typeof(T).GetProperties();
            string value = null;
            int num = cellNum.LastCellNum;

            for (int i = 1; i <= sheet.LastRowNum; i++)
            {
                IRow row = sheet.GetRow(i);
                var obj = new T();
                for (int j = 0; j < num; j++)
                {
                    value = row.GetCell(j).ToString();
                    string str = (propertys[j].PropertyType).FullName;
                    if (str == "System.String")
                    {
                        propertys[j].SetValue(obj, value, null);
                    }
                    else if (str == "System.DateTime")
                    {
                        DateTime pdt = Convert.ToDateTime(value, CultureInfo.InvariantCulture);
                        propertys[j].SetValue(obj, pdt, null);
                    }
                    else if (str == "System.Boolean")
                    {
                        bool pb = Convert.ToBoolean(value);
                        propertys[j].SetValue(obj, pb, null);
                    }
                    else if (str == "System.Int16")
                    {
                        short pi16 = Convert.ToInt16(value);
                        propertys[j].SetValue(obj, pi16, null);
                    }
                    else if (str == "System.Int32")
                    {
                        int pi32 = Convert.ToInt32(value);
                        propertys[j].SetValue(obj, pi32, null);
                    }
                    else if (str == "System.Int64")
                    {
                        long pi64 = Convert.ToInt64(value);
                        propertys[j].SetValue(obj, pi64, null);
                    }
                    else if (str == "System.Byte")
                    {
                        byte pb = Convert.ToByte(value);
                        propertys[j].SetValue(obj, pb, null);
                    }
                    else
                    {
                        propertys[j].SetValue(obj, null, null);
                    }
                }

                list.Add(obj);
            }

            return list;
        }

 

数据源是IFormFile类型的,因为前端导入文件的方法是用layui的。所以是IFormFile。

同样我们也不知道list<T>是什么类型的,所以我们还是得用反射去获取类型,而且这里还是写到内存里面,就不会在服务器上留下文件,不过服务器内存小的要注意一下。

然后我们也不知道属性是什么类型的,所以要 string str = (propertys[j].PropertyType).FullName; 

判断属性的数据类型,然后在从Excel中遍历每一个单元格,输出到list中就可以了,记得要留意下数据在第二行开始导入进去。

大致的问题就是这些,重点就是反射去获取list<T>的类型。关于Excel的操作,这里就不详细说明了。

总的来说,NPOI可以脱离微软Excel操作Excel实在是非常方便,oledb真的非常不好用。

最后,我还需要学习的很多,有什么错误请指出。谢谢大家!

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!