1. 程式人生 > >IT輪子系列(六)——Excel上傳與解析,一套代碼解決所有Excel業務上傳,你Get到了嗎

IT輪子系列(六)——Excel上傳與解析,一套代碼解決所有Excel業務上傳,你Get到了嗎

tryparse mappath src 個推 列名 import ges bject tab

前言

在日常開發當中,excel的上傳與解析是很常見的。根據業務不同,解析的數據模型也都不一樣。不同的數據模型也就需要不同的校驗邏輯,這往往需要寫多套的代碼進行字段的檢驗,如必填項,數據格式。為了避免重復編寫邏輯檢驗代碼,於是有了這篇文章。

第一步、讀取Excel表格數據

技術分享圖片
 1         public ActionResult UploadExcel()
 2         {
 3             ResultInfo<List<User>> result = new ResultInfo<List<User>>();            
4 5 var files = Request.Files; 6 var form = Request.Form; 7 if (files.Count > 0) 8 { 9 var file = files[0]; 10 //獲取文件擴展名 11 var ext = System.IO.Path.GetExtension(file.FileName); 12 var
newPath = ""; 13 try 14 { 15 //獲取文件路徑 16 var path = HttpContext.Server.MapPath("/Upload/Excels/"); 17 if (!System.IO.Directory.Exists(path)) 18 { 19 System.IO.Directory.CreateDirectory(path);
20 } 21 //保存文件 22 var newFileName = System.Guid.NewGuid().ToString("N") + ext;//新文件名 23 newPath = path + newFileName; 24 file.SaveAs(newPath); 25 DataTable dt = FileUtil.ExcelToDataTable(newPath, true); 26 result = ValidateExcelData<User>(dt); 27 } 28 catch (Exception ex) 29 { 30 result.IsError = true; 31 result.Msg = ex.Message; 32 } 33 finally 34 { 35 //清除文件 36 System.IO.File.Delete(newPath); 37 } 38 } 39 var obj = new { 40 success = result.IsError, 41 msg = result.Msg 42 }; 43 return Json(obj); 44 }
前端界面-上傳Excel

PS:這裏的上傳,使用了前面文章中關於文件上傳的,如有不明白,自行翻看前面IT輪子系列的文章拉。

關於excel的讀取,使用的是NPOI組件,網上關於NPOI的文章也很多,這裏也不再重復。代碼(這代碼也是從網上copy來的,已經找不到連接了,在這裏感謝一下這位博友): )如下:

技術分享圖片
  1         /// <summary>
  2         /// 將excel導入到datatable
  3         /// </summary>
  4         /// <param name="filePath">excel路徑</param>
  5         /// <param name="isColumnName">第一行是否是列名</param>
  6         /// <returns>返回datatable</returns>
  7         public static DataTable ExcelToDataTable(string filePath, bool isColumnName)
  8         {
  9             DataTable dataTable = null;
 10             FileStream fs = null;
 11             DataColumn column = null;
 12             DataRow dataRow = null;
 13             IWorkbook workbook = null;
 14             ISheet sheet = null;
 15             IRow row = null;
 16             ICell cell = null;
 17             int startRow = 0;
 18             try
 19             {
 20                 using (fs = File.OpenRead(filePath))
 21                 {
 22                     // 2007版本
 23                     if (filePath.IndexOf(".xlsx") > 0)
 24                         workbook = new XSSFWorkbook(fs);
 25                     // 2003版本
 26                     else if (filePath.IndexOf(".xls") > 0)
 27                         workbook = new HSSFWorkbook(fs);
 28 
 29                     if (workbook != null)
 30                     {
 31                         sheet = workbook.GetSheetAt(0);//讀取第一個sheet,當然也可以循環讀取每個sheet
 32                         dataTable = new DataTable();
 33                         if (sheet != null)
 34                         {
 35                             int rowCount = sheet.LastRowNum;//總行數
 36                             if (rowCount > 0)
 37                             {
 38                                 IRow firstRow = sheet.GetRow(0);//第一行
 39                                 int cellCount = firstRow.LastCellNum;//列數
 40 
 41                                 //構建datatable的列
 42                                 if (isColumnName)
 43                                 {
 44                                     startRow = 1;//如果第一行是列名,則從第二行開始讀取
 45                                     for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
 46                                     {
 47                                         cell = firstRow.GetCell(i);
 48                                         if (cell != null)
 49                                         {
 50                                             if (cell.StringCellValue != null)
 51                                             {
 52                                                 column = new DataColumn(cell.StringCellValue);
 53                                                 dataTable.Columns.Add(column);
 54                                             }
 55                                         }
 56                                     }
 57                                 }
 58                                 else
 59                                 {
 60                                     for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
 61                                     {
 62                                         column = new DataColumn("column" + (i + 1));
 63                                         dataTable.Columns.Add(column);
 64                                     }
 65                                 }
 66 
 67                                 //填充行
 68                                 for (int i = startRow; i <= rowCount; ++i)
 69                                 {
 70                                     row = sheet.GetRow(i);
 71                                     if (row == null) continue;
 72 
 73                                     dataRow = dataTable.NewRow();
 74                                     for (int j = row.FirstCellNum; j < cellCount; ++j)
 75                                     {
 76                                         cell = row.GetCell(j);
 77                                         if (cell == null)
 78                                         {
 79                                             dataRow[j] = "";
 80                                         }
 81                                         else
 82                                         {
 83                                             //CellType(Unknown = -1,Numeric = 0,String = 1,Formula = 2,Blank = 3,Boolean = 4,Error = 5,)
 84                                             switch (cell.CellType)
 85                                             {
 86                                                 case CellType.Blank:
 87                                                     dataRow[j] = "";
 88                                                     break;
 89                                                 case CellType.Numeric:
 90                                                     short format = cell.CellStyle.DataFormat;
 91                                                     //對時間格式(2015.12.5、2015/12/5、2015-12-5等)的處理
 92                                                     if (format == 14 || format == 31 || format == 57 || format == 58)
 93                                                         dataRow[j] = cell.DateCellValue;
 94                                                     else
 95                                                         dataRow[j] = cell.NumericCellValue;
 96                                                     break;
 97                                                 case CellType.String:
 98                                                     dataRow[j] = cell.StringCellValue;
 99                                                     break;
100                                             }
101                                         }
102                                     }
103                                     dataTable.Rows.Add(dataRow);
104                                 }
105                             }
106                         }
107                     }
108                 }
109                 return dataTable;
110             }
111             catch (Exception)
112             {
113                 if (fs != null)
114                 {
115                     fs.Close();
116                 }
117                 return null;
118             }
119         }
NPOI讀取Excel

第二步、使用泛型檢驗數據

這裏所說的通用,只不過是把檢驗的規則、配置放到了數據庫中。然後根據類名從數據庫讀取配置規則。在這篇文章中,配置是沒有使用到數據庫的,直接使用了一個配置類,代碼如下:

技術分享圖片
 1   /// <summary>
 2     /// excel導入配置表
 3     /// </summary>
 4     public class Sys_ExcelImportConfig
 5     {
 6         /// <summary>
 7         /// 表名
 8         /// </summary>
 9         public string TableName { get; set; }
10         /// <summary>
11         /// 表列名
12         /// </summary>
13         public string TableColumnName { get; set; }
14         /// <summary>
15         /// excel列名
16         /// </summary>
17         public string ExcelColumnName { get; set; }
18         /// <summary>
19         /// 數據類型
20         /// </summary>
21         public string DataType { get; set; }
22         /// <summary>
23         /// 是否必填項
24         /// </summary>
25         public bool IsRequired { get; set; }
26         /// <summary>
27         /// 是否值類型
28         /// </summary>
29         public bool IsValueType { get; set; }
30         /// <summary>
31         /// 是否檢驗數據格式,如手機號、email
32         /// </summary>
33         public bool IsDataChecked { get; set; }
34         /// <summary>
35         /// 正則表達式 若IsDataChecked為true則使用正則進行校驗
36         /// </summary>
37         public string Reg { get; set; }
38 
39     }
配置數據模型

PS:在實際項目中,可以在後臺添加一個配置界面。這個配置數據模型就對應數據庫中一個表。因此,如果系統需要導入多個業務模型的Excel 只需做好配置就OK拉。


數據解析的泛型方法

代碼如下:

技術分享圖片
  1         /// <summary>
  2         /// 驗證excel數據的格式
  3         /// </summary>
  4         /// <typeparam name="T">泛型</typeparam>
  5         /// <param name="dt">NPOI讀取的數據表</param>
  6         /// <returns>泛型結果集</returns>
  7         private ResultInfo<List<T>> ValidateExcelData<T>(DataTable dt) where T : new()
  8         {
  9             ResultInfo<List<T>> result = new ResultInfo<List<T>>();
 10             //從數據庫讀取本次導入的excel配置表
 11             /*
 12              * 這裏的demo就直接寫到程序裏,不做數據庫配置
 13              * 在實際項目中,list可以從一個表獲取,類的屬性就對應
 14              * 表中配置的字段
 15              */
 16             //1、定義配置數據源,這裏配置三列
 17             List<Sys_ExcelImportConfig> configList = new List<Sys_ExcelImportConfig> { 
 18                 new Sys_ExcelImportConfig{
 19                     TableName="User",
 20                     TableColumnName="Name",
 21                     DataType=typeof(System.String).FullName,
 22                     ExcelColumnName="姓名",
 23                     IsRequired = true
 24                 },
 25                 new Sys_ExcelImportConfig{
 26                     TableName="User",
 27                     TableColumnName="Position",
 28                     DataType=typeof(System.String).FullName,
 29                     ExcelColumnName="職位",
 30                     IsRequired = true
 31                 },
 32                 new Sys_ExcelImportConfig{
 33                     TableName="User",
 34                     TableColumnName="Age",
 35                     DataType=typeof(int).FullName,
 36                     ExcelColumnName="年齡",
 37                     IsValueType = true
 38                 }
 39             };
 40             //2、遍歷數據源
 41             var count = dt.Rows.Count;
 42             var isError = false;
 43             var msg = "";
 44             if (count > 0)
 45             {
 46                 //獲取所有的公共屬性
 47                 var proInfos = typeof(T).GetProperties();
 48                 //遍歷所有的行
 49                 for (int i = 0; i < count; i++)
 50                 {
 51                     T obj = new T();
 52                     /*
 53                      * 遍歷所有的配置列
 54                      * 不在配置列中都不導入
 55                      */
 56                     foreach (Sys_ExcelImportConfig config in configList)
 57                     {
 58                         var isContain = dt.Columns.Contains(config.ExcelColumnName);
 59                         if (isContain)//如果包含
 60                         {
 61                             //讀取該行該列的值
 62                             var value = dt.Rows[i][config.ExcelColumnName].ToString();
 63                             if (config.IsRequired)//是否為表填項
 64                             {
 65                                 if ("".Equals(value))//為空,退出循環體
 66                                 {
 67                                     isError = true;
 68                                     //返回錯誤信息
 69                                     msg = string.Format("Excel表中第{0}中{1}的值不允許為空", i, config.ExcelColumnName);
 70                                     break;
 71                                 }
 72                             }
 73                             if (config.IsValueType)//是否值類型:是,則驗證數據格式
 74                             {
 75                                 Type methodType = Type.GetType(config.DataType);
 76 
 77                                 //這裏關鍵的是& 引用類型的參數
 78                                 Type[] parameters = new Type[2] { typeof(string), Type.GetType(config.DataType + "&") };
 79                                 var method = methodType.GetMethod("TryParse", parameters);
 80                                 Object[] paraObjs = new Object[2];
 81                                 paraObjs[0] = value;                               
 82                                 var objResult = method.Invoke(null, paraObjs);
 83                                 //值類型是否轉換成功
 84                                 if (!(bool)objResult)
 85                                 {
 86                                     isError = true;
 87                                     //返回錯誤信息
 88                                     msg = string.Format("Excel表中第{0}行中[{1}]的值數據格式不正確", i + 1, config.ExcelColumnName);
 89                                     break;
 90                                 }
 91                             }
 92 
 93                             //給公共屬性並賦值
 94                             var property = proInfos.FirstOrDefault(t => t.Name == config.TableColumnName);
 95                             if (property != null)
 96                             {
 97                                 property.SetValue(obj, value);
 98                             }
 99                         }
100                     }
101                     if (isError)
102                     {
103                         //退出所有的循環
104                         break;
105                     }
106                     //驗證通過    
107                     //添加到數據列表
108                     result.Data.Add(obj);
109                 }
110             }
111             /*
112              * 這裏可以返回isError和msg 
113              */
114             result.IsError = isError;
115             result.Msg = msg;
116             return result;
117         }
泛型解析方法

代碼中註釋寫的很詳細,如有看不明白,歡迎砸磚頭和留言......

對於其他業務的EXCEL上傳,只需在後臺做配置就可以了,將數據模型傳到方法中,如demo中的User. 在拿到返回的List後 可以做進一步的處理,如寫入到數據庫。在實際項目中,為了做到共用,可以將這個泛型方法放到一個common項目中,這樣別的項目可以直接引用這個common項目。

技術分享圖片
 1  public class ResultInfo<T>
 2     {
 3         public ResultInfo()
 4         {
 5             IsError = false;
 6             Msg = "操作成功";
 7             Data = default(T);
 8         }
 9         /// <summary>
10         /// 狀態true/false
11         /// </summary>
12         public bool IsError { get; set; }
13         /// <summary>
14         /// 結果信息
15         /// </summary>
16         public string Msg { get; set; }
17         /// <summary>
18         /// 數據
19         /// </summary>
20         public T Data { get; set; }
21     }
ResultInfo數據模型

後記

從10月初,確切的說9月29號起,也寫了7、8篇技術類文章。有的文章也有幾百的閱讀量,可評論留言的人卻少之又少,點贊、推薦就更沒有。如果這些文章確實幫到了你,對你的工作有那麽一點點的用,希望路過的兄弟姐妹們可以有贊的點個贊,有推薦的來個推薦,有轉載的來個轉載,為我這個博客園增添點人氣。

謝謝拉。。。。。。。 GOOD NIGHT.

ps:最後來張閱讀量的截圖:

技術分享圖片

IT輪子系列(六)——Excel上傳與解析,一套代碼解決所有Excel業務上傳,你Get到了嗎