基於NPOI的Excel數據導入

来源:http://www.cnblogs.com/xuanbg/archive/2016/11/12/6057536.html
-Advertisement-
Play Games

從Excel導入數據最令人頭疼的是數據格式的相容性,特別是日期類型的相容性。為了能夠無腦導入日期,折騰了一天的NPOI。在經過測試確實可以導入任意格式的合法日期後,寫下這篇小文,與大家共用。完整代碼請移步:https://github.com/xuanbg/Utility 2016-11-13 04 ...


從Excel導入數據最令人頭疼的是數據格式的相容性,特別是日期類型的相容性。為了能夠無腦導入日期,折騰了一天的NPOI。在經過測試確實可以導入任意格式的合法日期後,寫下這篇小文,與大家共用。完整代碼請移步:https://github.com/xuanbg/Utility

2016-11-13 04:06 修正一個bug。由try DateCellValue改為判斷列數據類型,如類型為DateTiime返回DateCellValue,否則返回NumericCellValue或StringCellValue。

概述:

這個幫助類是一個泛型類,泛型參數對應的實體類還起到模板的作用。如果你的Excel文件使用與實體類不同的列標題的話,可以通過給屬性加上Alias特性,將列標題和屬性進行對應。例如:

Excel格式如圖:

實體類:

 1 using System;
 2 using Insight.Utils.Common;
 3 
 4 namespace Insight.WS.Server.Common.Entity
 5 {
 6     public class Logistics
 7     {
 8         [Alias("訂單號")]
 9         public string OrderCode { get; set; }
10 
11         [Alias("物流公司")]
12         public string Service { get; set; }
13 
14         [Alias("物流單號")]
15         public string Number { get; set; }
16 
17         [Alias("發貨時間")]
18         public DateTime DeliveryTime { get; set; }
19     }
20 }

返回的Json:

 1 [
 2   {
 3     "OrderCode": "201611S1200324",
 4     "Service": "順豐",
 5     "Number": "33012231F54351",
 6     "DeliveryTime": "2016-11-10T11:02:44"
 7   },
 8   {
 9     "OrderCode": "2016111200324",
10     "Service": "順豐",
11     "Number": "33012231F54352",
12     "DeliveryTime": "2016-11-12T09:02:44"
13   },
14   {
15     "OrderCode": "2016111200324",
16     "Service": "EMS",
17     "Number": "33012231F54353",
18     "DeliveryTime": "2016-11-12T09:02:44"
19   }
20 ]

 

1、類主體,負責根據傳入的文件路徑讀取數據,並調用其他私有方法對數據進行處理。最後轉換成List<T>並序列化成Json返回。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Data;
 4 using System.IO;
 5 using Insight.Utils.Entity;
 6 using NPOI.SS.UserModel;
 7 
 8 namespace Insight.Utils.Common
 9 {
10     public class NpoiHelper<T> where T : new()
11     {
12         private readonly Result _Result = new Result();
13 
14         /// <summary>
15         /// 導入Excel文件
16         /// </summary>
17         /// <param name="path">文件路徑</param>
18         /// <param name="index">Sheet索引</param>
19         /// <returns>Result</returns>
20         public Result Import(string path, int index = 0)
21         {
22             if (!File.Exists(path))
23             {
24                 _Result.FileNotExists();
25                 return _Result;
26             }
27 
28             IWorkbook book;
29             using (var file = new FileStream(path, FileMode.Open, FileAccess.Read))
30             {
31                 book = WorkbookFactory.Create(file);
32             }
33 
34             if (index >= book.NumberOfSheets)
35             {
36                 _Result.SheetNotExists();
37                 return _Result;
38             }
39 
40             var sheet = book.GetSheetAt(index);
41             var table = GetSheetData(sheet);
42             var list = Util.ConvertToList<T>(table);
43             _Result.Success(list);
44             return _Result;
45         }
46     }
47 }

 

2、GetSheetData方法,負責將Sheet中的數據讀取到DataTable。這裡通過實體類屬性的特性值作為列名,屬性類型作為列數據類型來初始化DataTable。當然,首行是例外,因為首行是列標題而非數據。

 1         /// <summary>
 2         /// 讀取Sheet中的數據到DataTable
 3         /// </summary>
 4         /// <param name="sheet">當前數據表</param>
 5         /// <returns>DataTable</returns>
 6         private DataTable GetSheetData(ISheet sheet)
 7         {
 8             var table = InitTable(sheet);
 9             if (table == null) return null;
10 
11             var rows = sheet.GetEnumerator();
12             while (rows.MoveNext())
13             {
14                 var row = (IRow) rows.Current;
15                 if (row.RowNum == 0) continue;
16 
17                 var dr = table.NewRow();
18                 for (var i = 0; i < table.Columns.Count; i++)
19                 {
20                     try
21                     {
22                         var type = table.Columns[i].DataType;
23                         dr[i] = GetCellData(row.GetCell(i), type);
24                     }
25                     catch (Exception)
26                     {
27                         dr[i] = DBNull.Value;
28                     }
29 
30                 }
31                 table.Rows.Add(dr);
32             }
33 
34             return table;
35         }

初始化DataTable的方法:

 1         /// <summary>
 2         /// 初始化DataTable
 3         /// </summary>
 4         /// <param name="sheet">當前數據表</param>
 5         /// <returns>DataTable</returns>
 6         private DataTable InitTable(ISheet sheet)
 7         {
 8             var title = sheet.GetRow(0);
 9             if (title == null)
10             {
11                 _Result.NoRowsRead();
12                 return null;
13             }
14 
15             try
16             {
17                 var dict = GetDictionary();
18                 var table = new DataTable();
19                 foreach (var cell in title.Cells)
20                 {
21                     var col_name = cell.StringCellValue;
22                     var col_type = dict[col_name];
23                     table.Columns.Add(cell.StringCellValue, col_type);
24                 }
25 
26                 return table;
27             }
28             catch
29             {
30                 _Result.IncorrectExcelFormat();
31                 return null;
32             }
33         }

生成模板字典的方法:

 1         /// <summary>
 2         /// 獲取指定類型的屬性名稱/類型字典
 3         /// </summary>
 4         /// <returns>Dictionary</returns>
 5         private Dictionary<string, Type> GetDictionary()
 6         {
 7             var dict = new Dictionary<string, Type>();
 8             var propertys = typeof(T).GetProperties();
 9             foreach (var p in propertys)
10             {
11                 string name;
12                 var attributes = p.GetCustomAttributes(typeof(AliasAttribute), false);
13                 if (attributes.Length > 0)
14                 {
15                     var type = (AliasAttribute)attributes[0];
16                     name = type.Alias;
17                 }
18                 else
19                 {
20                     name = p.Name;
21                 }
22 
23                 dict.Add(name, p.PropertyType);
24             }
25 
26             return dict;
27         }

 

3、重點來了!

因為日期/時間在Excel中可能被表示為文本格式或日期格式(其實是Numeric類型),所以在CellType為String/Numeric的時候,如果列數據類型為DateTime,則取cell的DateCellValue,否則取cell的StringCellValue/NumericCellValue就好了。

這樣,無論日期是文本或日期格式,都可以完美獲取。

 1         /// <summary>
 2         /// 讀Excel單元格的數據
 3         /// </summary>
 4         /// <param name="cell">Excel單元格</param>
 5         /// <param name="type">列數據類型</param>
 6         /// <returns>object 單元格數據</returns>
 7         private object GetCellData(ICell cell, Type type)
 8         {
 9             switch (cell.CellType)
10             {
11                 case CellType.Numeric:
12                     if (type == typeof(DateTime)) return cell.DateCellValue;
13 
14                     return cell.NumericCellValue;
15 
16                 case CellType.String:
17                     if (type == typeof(DateTime)) return cell.DateCellValue;
18 
19                     return cell.StringCellValue;
20 
21                 case CellType.Boolean:
22                     return cell.BooleanCellValue;
23 
24                 case CellType.Unknown:
25                 case CellType.Formula:
26                 case CellType.Blank:
27                 case CellType.Error:
28                     return null;
29                 default:
30                     return null;
31             }
32         }

 

 4、DataTable轉成List<T>的方法:

 1         /// <summary>
 2         /// 將DataTable轉為List
 3         /// </summary>
 4         /// <param name="table">DataTable</param>
 5         /// <returns>List</returns>
 6         public static List<T> ConvertToList<T>(DataTable table) where T: new()
 7         {
 8             var list = new List<T>();
 9             var propertys = typeof(T).GetProperties();
10             foreach (DataRow row in table.Rows)
11             {
12                 var obj = new T();
13                 foreach (var p in propertys)
14                 {
15                     string name;
16                     var attributes = p.GetCustomAttributes(typeof(AliasAttribute), false);
17                     if (attributes.Length > 0)
18                     {
19                         var type = (AliasAttribute) attributes[0];
20                         name = type.Alias;
21                     }
22                     else
23                     {
24                         name = p.Name;
25                     }
26 
27                     if (table.Columns.Contains(name))
28                     {
29                         if (!p.CanWrite) continue;
30 
31                         var value = row[name];
32                         if (value == DBNull.Value) value = null;
33 
34                         p.SetValue(obj, value, null);
35                     }
36                 }
37                 list.Add(obj);
38             }
39             return list;
40         }

自定義特性:

 1 using System;
 2 
 3 namespace Insight.Utils.Common
 4 {
 5     [AttributeUsage(AttributeTargets.Property)]
 6     public class AliasAttribute : Attribute
 7     {
 8         /// <summary>
 9         /// 屬性別名
10         /// </summary>
11         public string Alias { get; }
12 
13         /// <summary>
14         /// 構造方法
15         /// </summary>
16         /// <param name="alias">別名</param>
17         public AliasAttribute(string alias)
18         {
19             Alias = alias;
20         }
21     }
22 }

 

 

請大家對此多發表意見和建議,謝謝。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • docker部署環境:CentOS release 6.5 (Final) Docker配置文件:/etc/sysconfig/docker 重要參數解釋: -H 表示Docker Daemon綁定的地址 -H unix:///var/run/docker.sock -H tcp://0.0.0.0 ...
  • 一、Linux操作系統簡介 1、Linux系統定義:Linux是一套免費使用和自由傳播的類Unix操作系統,是一個基於POSIX和UNIX的多用戶、多任務、支持多線程和多CPU的操作系統 2、Linux系統運行穩定,主要用於伺服器。 3、Linux系統用戶分為: a、系統用戶root:提示符# b、 ...
  • 1.GFS介紹 GFS簡要說明,它有兩種: 1. Google文件系統:GFS是GOOGLE實現的是一個可擴展的分散式文件系統,用於大型的、分散式的、對大量數據進行訪問的應用。它運行於廉價的普通硬體上,但可以提供容錯功能。它可以給大量的用戶提供總體性能較高的服務。欲瞭解更多,可以訪問:http:// ...
  • 題目描述 Description 在小松宿舍樓下的不遠處,有PK大學最不錯的一個食堂——The Farmer’s Canteen(NM食堂)。由於該食堂的菜都很不錯,價格也公道,所以很多人都喜歡來這邊吃飯。The Farmer’s Canteen的點菜方式如同在超市自選商品一樣,人們從一個指定的路口 ...
  • 公司的同事離職了,接下來的日子可能會忙碌,能完善DEMO的時間也會少了,因此,把做的簡易DEMO整體先記錄一下,等後續不斷的完善。 參考兩位大神的日誌:WEB版微信協議部分功能分析、【完全開源】微信客戶端.NET版 尤其是周見智大神的DEMO,因為好多和微信的服務端交互,都借鑒了大神的源碼,幫助巨大 ...
  • 首先對項目添加名為Microsoft.VisualBasic.dll的引用,然後添加命名空間using Microsoft.VisualBasic.FileIO;usingSystem;namespaceleaver{ classProgram { staticvoidMain(string[]ar... ...
  • No.1 準備應用程式 1. 創建.Net Core Web項目 2. 使用VS2015發佈 No.2 安裝.Net Core for Ubuntu Ubuntu的安裝就不介紹了,百度搜索一大堆。本人用的VMWare,裝好Tools很方便。 1. 添加dotnet源 2. 安裝.Net Core S ...
  • C# 事件 【博主】反骨仔 【原文】http://www.cnblogs.com/liqingwen/p/6057301.html 序 之前通過《C# 知識回顧 - 委托 delegate》、《C# 知識回顧 - 委托 delegate (續)》介紹了委托的基本知識,這次我們來看看事件。 目錄 C# ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...