在工作中很多要把EXECL的数据取出来的情况,因此,写一个相对通用的这种功能类很有必要.我因为当时只考虑了引用MS的EXECL类的情况,所以后面加入NPOI时这个类就有点显乱了,因为已经有人在用了,只能以后考虑修改了.MS的因为DLL的版本问题实在是个大麻烦,所以MS的EXECL类会全部用反射来调用.
1.一个辅助类.主要用于属性名与EXECL栏位的对应.
public class ColumnIndexProperty { public int Index { get; set; } public string PropertyName { get; set; } public PropertyInfo Property { get; set; } public string SIndex { get { if (Index >= 1 && Index <= 702) { string result = string.Empty; int decade = Index / 26; int units = Index % 26; if (decade != 0) result += AsciiIntToString(decade + 64); result += AsciiIntToString(units + 64); return result; } else { throw new Exception("EXECL的栏位要求在1-702之间."); } } set { value = value.ToUpper(); char[] chars = value.ToCharArray(); if (chars.Length == 1) { Index = AsciiStringToInt(value) - 64; } else if (chars.Length == 2) { int decade = AsciiStringToInt(chars[0].ToString()) - 64; int units = AsciiStringToInt(chars[1].ToString()) - 64; Index = decade * 26 + units; } else { throw new Exception("EXECL的栏位要求在A-ZZ之间."); } } } private string AsciiIntToString(int ascii) { System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding(); byte[] byteArray = new byte[] { (byte)ascii }; return asciiEncoding.GetString(byteArray); } private int AsciiStringToInt(String ch) { System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding(); return asciiEncoding.GetBytes(ch)[0]; } }
2. 主要导入代码.
public class ReadExecl { static ReadExecl() { // //TODO: 在此处添加构造函数逻辑 // } public ReadExecl() { Top = 1; workIndex = 1; IsNopi = true; NotIndexs = new List<int>().ToArray(); Cps = new List<ColumnIndexProperty>(); } #region "基本属性设置" //设置要开始读取的行数 public int Top { get; set; } //导入方式 public bool IsNopi { get; set; } //这个属性现暂时无用,因为读取列会从Cps里读取的. public int Left { get; set; } //设置栏位的序列与对应属性的对应 public List<ColumnIndexProperty> Cps { get; set; } //文件名 private string filePath; public string FilePath { get { return filePath; } set { double version = 0; if (!File.Exists(value)) { throw new Exception("路径错误,请仔细检察."); } string protyery = value.Substring(value.LastIndexOf('.') + 1); if (protyery.ToUpper() != "XLS" && protyery.ToUpper() != "XLSX") { File.Delete(value); throw new Exception("不支持的文件格式."); } if (!IsNopi) { double.TryParse(Version, out version); if (version <= 11 && protyery.ToUpper() == "XLSX") { File.Delete(value); throw new Exception("文件版本格式过高,请升级服务器上的EXECL版本或降低EXECL文件版本."); } } filePath = value; } } //在EXECL上的第几张表里 private int workIndex; public int WorksheetIndex { get { if (workIndex == 0) return 1; return workIndex; } set { //EXECL的起头并不是如C#等编程语言里的0开头,全是以1开头,页面数,行数,列数等 if (value < 1) throw new Exception("不能少于1"); workIndex = value; } } //设置那些行不能为空 public string NotNull { set { string[] ns = value.Split(','); List<int> ls = new List<int>(); foreach (var n in ns) { int result = 0; if (int.TryParse(n, out result)) { ls.Add(result); } else { ColumnIndexProperty p = new ColumnIndexProperty() { SIndex = n }; ls.Add(p.Index); } } NotIndexs = ls.Distinct().ToArray(); } } //设置导入文件的地方信息 public CultureInfo CultureInfo { get; set; } #endregion #region "一些帮助属性" public string ErrorMassage { get; set; } public int[] NotIndexs { get; set; } public string[] NotSIndex { get { List<string> ls = new List<string>(); foreach (var index in NotIndexs) { ColumnIndexProperty p = new ColumnIndexProperty() { Index = index }; ls.Add(p.SIndex); } return ls.ToArray(); } set { List<int> ls = new List<int>(); foreach (var sindex in value) { ColumnIndexProperty p = new ColumnIndexProperty() { SIndex = sindex }; ls.Add(p.Index); } NotIndexs = ls.ToArray(); } } #endregion #region "类私有的帮助函数" private string pathDate() { string fileName = FilePath.Substring(FilePath.LastIndexOf(@"\") + 1); string Path = FilePath.Substring(0, FilePath.LastIndexOf(@"\")); string[] bs = fileName.Split('.'); fileName = bs[0] + DateTime.Now.ToString("yyyyMMddHHmmss") + ".txt"; return Path + @"\" + fileName; } /// <summary> /// 把PropertyInfo和实体T的属性对应 /// </summary> /// <typeparam name="T"></typeparam> private void FullColumnIndexProperty<T>() { //得到对应实体的属性 PropertyInfo[] ps = typeof(T).GetProperties(); //把属性与对应EXECL上的表栏位对应起来 foreach (var cp in Cps) { //把属性与栏位对应上.没有对应上会直接抛出异常 cp.Property = ps.First(t => t.Name == cp.PropertyName); } } /// <summary> /// 杀掉相应的EXECL进程 /// </summary> /// <param name="excel"></param> /// <param name="StartTime"></param> /// <param name="EndTime"></param> private void Kill(object excel, DateTime StartTime, DateTime EndTime) { if (excel == null) { return; } int hwnd = 0; try { hwnd = (int)excel.GetType().InvokeMember("Hwnd", BindingFlags.GetProperty, null, excel, null); } catch { hwnd = 0; } IntPtr t = new IntPtr(hwnd); int k = 0; GetWindowThreadProcessId(t, out k); if (k != 0) { Process f = Process.GetProcessById(k); f.Kill(); } else { //这个方法如果同时有多个人处理EXECL就会出问题,可能关闭掉本不应该关闭掉的EXECL进程 foreach (Process p in Process.GetProcesses().Where( p => p.ProcessName.StartsWith("EXCEL", StringComparison.OrdinalIgnoreCase))) { if (p.StartTime >= StartTime && p.StartTime <= EndTime) { p.Kill(); } } } } /// <summary> /// 把EXECL文件对应表的数据转换成TXT文件并返回对应TXT文件的路径 /// </summary> /// <returns></returns> private string TransformFile() { string fileName = this.pathDate(); DateTime StartTime = DateTime.Now; //打开EXECL Type objExcelType = Type.GetTypeFromProgID("Excel.Application"); if (objExcelType == null) { throw new Exception("没有安装EXECL."); } object objApp = Activator.CreateInstance(objExcelType); if (objApp == null) { throw new Exception("不能创建EXECL进程."); } DateTime EndTime = DateTime.Now; //获取Workbook集 try { object objBooks = objApp.GetType().InvokeMember("Workbooks", BindingFlags.GetProperty, null, objApp, null); //第二个参数为0表示不更新任何链接 object[] os = new object[]{FilePath, 0, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing}; objBooks.GetType().InvokeMember("Open", BindingFlags.InvokeMethod, null, objBooks, os); object worksheets = objApp.GetType().InvokeMember("Worksheets", BindingFlags.GetProperty, null, objApp, null); object sheet = objBooks.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, worksheets, new object[1] { workIndex }); // Excel.XlFileFormat.xlUnicodeText 为42,保存为Unicode字符集的TXT文件 object[] savas = new object[] { fileName, 42, Type.Missing, Type.Missing, false, false, 1, Type.Missing, Type.Missing, Type.Missing}; sheet.GetType().InvokeMember("SaveAs", BindingFlags.InvokeMethod, null, sheet, savas); System.Runtime.InteropServices.Marshal.ReleaseComObject(sheet); System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheets); System.Runtime.InteropServices.Marshal.ReleaseComObject(objBooks); objApp.GetType().InvokeMember("Quit", BindingFlags.InvokeMethod, null, objApp, null); System.Runtime.InteropServices.Marshal.ReleaseComObject(objApp); GC.Collect(); } catch (Exception e) { throw new Exception(e.Message); } finally { Kill(objApp, StartTime, EndTime); //以免出现这个文件已经有个进程在用的异常(等待EXECL关闭) Thread currentThread = Thread.CurrentThread; currentThread.Join(500); } return fileName; } /// <summary> /// 把String类型的值转化成对应的类型 /// </summary> /// <param name="value"></param> /// <param name="conversionType"></param> /// <returns></returns> private object ChangeTypeTo(string value, Type conversionType) { if (conversionType == null) throw new ArgumentNullException("conversionType"); if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { if (string.IsNullOrEmpty(value)) return null; NullableConverter nullableConverter = new NullableConverter(conversionType); conversionType = nullableConverter.UnderlyingType; } if (conversionType == typeof(Guid)) { return new Guid(value); } else if (conversionType == typeof(int)) { value = value.Replace(",", ""); value = value.Trim(',', '"'); if (string.IsNullOrEmpty(value)) return 0; return int.Parse(value); } else if (conversionType == typeof(DateTime)) { if (CultureInfo == null) return DateTime.Parse(value); else return DateTime.Parse(value, CultureInfo); } else if (conversionType == typeof(string)) { return value; } return Convert.ChangeType(value, conversionType); } /// <summary> /// NOPI要转文件的转化 /// </summary> /// <param name="cell"></param> /// <param name="conversionType"></param> /// <returns></returns> private object ChangeTypeTo(NPOI.SS.UserModel.Cell cell, Type conversionType) { if (conversionType == null) throw new ArgumentNullException("conversionType"); if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { if (cell == null) return null; NullableConverter nullableConverter = new NullableConverter(conversionType); conversionType = nullableConverter.UnderlyingType; } if (conversionType == typeof(string) && (cell == null || string.IsNullOrEmpty(cell.ToString()))) return null; if (conversionType == typeof(Guid)) { return new Guid(cell.ToString()); } else if (conversionType == typeof(DateTime)) { if (cell.CellType == NPOI.SS.UserModel.CellType.NUMERIC) return cell.DateCellValue; else { if (CultureInfo == null) return DateTime.Parse(cell.StringCellValue); else return DateTime.Parse(cell.StringCellValue, CultureInfo); } } else if (cell.CellType == NPOI.SS.UserModel.CellType.NUMERIC) { return Convert.ChangeType(cell.NumericCellValue, conversionType); } else { return Convert.ChangeType(cell.StringCellValue, conversionType); } } #endregion #region "导出EXECL的数据" /// <summary> /// 设置好相关属性后取出EXECL的数据并转化成相应的类的对象 /// </summary> /// <typeparam name="T">要得到数据的类型</typeparam> /// <returns>返回EXECL对应的对象集</returns> public List<T> GetExecl<T>() where T : new() { this.ErrorMassage = string.Empty; if (IsNopi) return GetNExecl<T>(); else return GetLExecl<T>(); } /// <summary> /// 设置好相关属性后取出EXECL的数据并转化成相应的类的对象 /// </summary> /// <typeparam name="T">要得到数据的类型</typeparam> /// <param name="notIndexs">不能为空的栏位索引</param> /// <returns>返回EXECL对应的对象集</returns> public List<T> GetLExecl<T>() where T : new() { string file = TransformFile(); List<T> data = new List<T>(); StreamReader sr = new StreamReader(file); try { for (int i = 1; i < Top; i++) { sr.ReadLine(); } //得到对应实体的属性 FullColumnIndexProperty<T>(); string line = string.Empty; int currentLine = Top - 1; int max = Cps.Max(p => p.Index); while ((line = sr.ReadLine()) != null) { currentLine++; string[] split = line.Split('\t'); if (max > split.Count()) continue; bool result = true; foreach (int index in NotIndexs) { if (index > 0 && string.IsNullOrEmpty(split[index - 1])) result = false; } if (!result) continue; T t = new T(); foreach (var cp in Cps) { try { string value = split[cp.Index - 1]; object va = ChangeTypeTo(value.Trim(), cp.Property.PropertyType); cp.Property.SetValue(t, va, null); } catch (Exception e) { this.ErrorMassage += string.Format("Execl文件第{0}行第{1}栏有错误为:{2}<br>", currentLine, cp.SIndex, e.Message); continue; } } data.Add(t); } return data; } catch (Exception e) { this.ErrorMassage += e.Message + "<br>"; return null; } finally { sr.Close(); File.Delete(file); } } /// <summary> /// 通过NOPI的方式导入. /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public List<T> GetNExecl<T>() where T : new() { //填入属性和栏位的关系 FullColumnIndexProperty<T>(); List<T> data = new List<T>(); Stream filestream = new FileStream(FilePath, FileMode.Open); //打开EXECL文件 HSSFWorkbook workbook = new HSSFWorkbook(filestream); //打开对应的EXECL表 var worksheet = workbook.GetSheetAt(WorksheetIndex - 1); int start = Top - 1; int end = worksheet.LastRowNum; //检查栏位是否为空 Func<int, int, bool> IsNull = (row, col) => { var cell = worksheet.GetRow(row).GetCell(col); return cell == null || string.IsNullOrEmpty(cell.ToString()); }; //开始查找EXECL文件数据. for (int i = start; i <= end; i++) { bool result = true; foreach (int index in NotIndexs) { if (IsNull(i, index - 1)) result = false; } if (!result) continue; T t = new T(); foreach (var cp in Cps) { try { object value = this.ChangeTypeTo(worksheet.GetRow(i).GetCell(cp.Index - 1), cp.Property.PropertyType); cp.Property.SetValue(t, value, null); } catch (Exception e) { this.ErrorMassage += string.Format("Execl文件第{0}行第{1}栏有错误为:{2}<br>", i + 1, cp.SIndex, e.Message); continue; } } data.Add(t); } return data; } #endregion #region "Execl信息帮助和获得" public List<int> ErrorLine { get; set; } [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID); public override string ToString() { string vell = " "; string fileName = FilePath.Substring(FilePath.LastIndexOf(@"\") + 1); string message = string.Empty; // message += "文件名:" + fileName + "." + vell; message += string.Format("读取当前文件的第{0}张表.{1}", this.workIndex, vell); message += string.Format("从第{0}行开始读入数据.{1}读取的栏位索引", Top, vell); foreach (var cp in Cps) { message += cp.SIndex.ToString() + "格式为"; message += GetTypeInfo(cp.Property.PropertyType) + ","; } message = message.Substring(0, message.LastIndexOf(",")); message += "."; if (NotIndexs.Length == 0 || NotIndexs[0] == 0) return message; message += vell + "以下栏位索引"; foreach (var index in this.NotSIndex) { message += index + ","; } message = message.Substring(0, message.LastIndexOf(",")); message += "不能为空."; return message; } public static string GetTypeInfo(Type type) { type = type.ChangeTypeTo(); string result = "末知"; if (type == typeof(Int32) || type == typeof(Int16) || type == typeof(Int64)) result = "整数"; else if (type == typeof(DateTime)) result = "时间"; else if (type == typeof(float) || type == typeof(decimal) || type == typeof(double)) result = "精确数"; else if (type == typeof(Guid)) result = "GUID"; else if (type == typeof(bool)) result = "布尔值"; else if (type == typeof(byte[])) result = "字符数组"; else result = "字符串"; return result; } public string Version { get { DateTime start = DateTime.Now; Type objExcelType = Type.GetTypeFromProgID("Excel.Application"); if (objExcelType == null) { throw new Exception("没有安装EXECL."); } object objApp = Activator.CreateInstance(objExcelType); if (objApp == null) { throw new Exception("不能创建EXECL进程."); } DateTime end = DateTime.Now; string version = objApp.GetType().InvokeMember("Version", BindingFlags.GetProperty, null, objApp, null) as string; Kill(objApp, start, end); return version; } } #endregion #region "如果有配置文件,从配置文件取数据" /// <summary> /// 专用于读配置文件时给对应对象加上标识 /// </summary> public string Name { get; set; } private static List<ReadExecl> readExecls; /// <summary> /// 读配置文件的信息 /// </summary> private static void GetPageConfig() { //配置文件固定在应用程序的根目录并且文件名为ExeclDownFileConfig.xml string configPath = HttpContext.Current.Server.MapPath("~/") + "ExeclDownFileConfig.xml"; if (!File.Exists(configPath)) throw new Exception("没有对应的EXECL文件配置."); //加载XML文件. var xdoc = XDocument.Load(configPath); List<ReadExecl> res = new List<ReadExecl>(); //浏览XML文件的节点. foreach (var a in xdoc.Descendants("ExeclFile")) { ReadExecl readexecl = new ReadExecl(); if (a.Element("Name") != null) readexecl.Name = a.Element("Name").Value; if (a.Element("Top") != null) readexecl.Top = int.Parse(a.Element("Top").Value); if (a.Element("WorksheetIndex") != null) readexecl.WorksheetIndex = int.Parse(a.Element("WorksheetIndex").Value); if (a.Element("NotNull") != null) readexecl.NotNull = a.Element("NotNull").Value; if (a.Element("CultureInfo") != null) readexecl.CultureInfo = new CultureInfo(a.Element("CultureInfo").Value); if (a.Element("IsNopi") != null && a.Element("IsNopi").Value.ToUpper() != "TRUE") readexecl.IsNopi = false; if (a.Element("FilePath") != null) readexecl.FilePath = a.Element("FilePath").Value; foreach (var b in a.Element("ExeclColumns").Descendants("ExeclColumn")) { int i = 0; string index = b.Element("Index").Value; string propertyname = b.Element("Propertyname").Value; if (int.TryParse(index, out i)) readexecl.Cps.Add(new ColumnIndexProperty() { Index = i, PropertyName = propertyname }); else readexecl.Cps.Add(new ColumnIndexProperty() { SIndex = index, PropertyName = propertyname }); } res.Add(readexecl); } readExecls = res; } /// <summary> /// 外部接口用于得到存储配置文件的信息 /// </summary> public static List<ReadExecl> ReadExecls { get { if (readExecls == null) { GetPageConfig(); } return readExecls; } } /// <summary> /// 根据对应对象的标识得到相应的对象 /// </summary> /// <param name="name"></param> /// <returns></returns> public static ReadExecl GetReadExecl(string name) { return ReadExecls.First(p => p.Name.ToUpper() == name.ToUpper()); } #endregion }
3.xml文件配置如下(ps:配置文件固定在应用程序的根目录并且文件名为ExeclDownFileConfig.xml)
<?xml version="1.0" encoding="utf-16"?> <ReadExeclFile> <Project>PEM</Project> <ExeclFiles> <ExeclFile> <Name>TRP_CustomerOrder</Name> <WorksheetIndex>1</WorksheetIndex> <Top>11</Top> <NotNull>1</NotNull> <ExeclColumns> <ExeclColumn> <Index>A</Index> <Propertyname>Serial</Propertyname> </ExeclColumn> <ExeclColumn> <Index>2</Index> <Propertyname>Customer</Propertyname> </ExeclColumn> <ExeclColumn> <Index>3</Index> <Propertyname>OrderNo</Propertyname> </ExeclColumn> <ExeclColumn> <Index>4</Index> <Propertyname>MachineType</Propertyname> </ExeclColumn> <ExeclColumn> <Index>5</Index> <Propertyname>ProductNO</Propertyname> </ExeclColumn> <ExeclColumn> <Index>G</Index> <Propertyname>Quantity</Propertyname> </ExeclColumn> <ExeclColumn> <Index>I</Index> <Propertyname>ReqDate</Propertyname> </ExeclColumn> </ExeclColumns> <IsNopi>True</IsNopi> </ExeclFile> <ExeclFile> <Name>TRP_Inventory</Name> <WorksheetIndex>1</WorksheetIndex> <Top>4</Top> <NotNull>1</NotNull> <ExeclColumns> <ExeclColumn> <Index>1</Index> <Propertyname>TNumber</Propertyname> </ExeclColumn> <ExeclColumn> <Index>2</Index> <Propertyname>Description</Propertyname> </ExeclColumn> <ExeclColumn> <Index>11</Index> <Propertyname>StockNT360</Propertyname> </ExeclColumn> <ExeclColumn> <Index>12</Index> <Propertyname>StockUT360</Propertyname> </ExeclColumn> </ExeclColumns> <IsNopi>True</IsNopi> </ExeclFile> <ExeclFile> <Name>TRP_OpenPO</Name> <WorksheetIndex>1</WorksheetIndex> <Top>2</Top> <CultureInfo>fr-FR</CultureInfo> <ExeclColumns> <ExeclColumn> <Index>1</Index> <Propertyname>OrderNumber</Propertyname> </ExeclColumn> <ExeclColumn> <Index>3</Index> <Propertyname>OrederDate</Propertyname> </ExeclColumn> <ExeclColumn> <Index>7</Index> <Propertyname>TNumber</Propertyname> </ExeclColumn> <ExeclColumn> <Index>14</Index> <Propertyname>UM</Propertyname> </ExeclColumn> <ExeclColumn> <Index>37</Index> <Propertyname>OriginalOrderedAmount</Propertyname> </ExeclColumn> </ExeclColumns> <IsNopi>True</IsNopi> </ExeclFile> <ExeclFile> <Name>TRP_ProducingOrder</Name> <WorksheetIndex>1</WorksheetIndex> <Top>3</Top> <NotNull>G,14,16</NotNull> <ExeclColumns> <ExeclColumn> <Index>1</Index> <Propertyname>Machine</Propertyname> </ExeclColumn> <ExeclColumn> <Index>7</Index> <Propertyname>PartNO</Propertyname> </ExeclColumn> <ExeclColumn> <Index>10</Index> <Propertyname>Serial</Propertyname> </ExeclColumn> <ExeclColumn> <Index>N</Index> <Propertyname>QtyUnfinished</Propertyname> </ExeclColumn> <ExeclColumn> <Index>P</Index> <Propertyname>PlannedOnMachineDate</Propertyname> </ExeclColumn> </ExeclColumns> <IsNopi>True</IsNopi> </ExeclFile> </ExeclFiles> </ReadExeclFile>
4.用法.
//Exevl导入方式 ReadExecl re = new ReadExecl(); re.Top = 11; re.FilePath = fileName; re.WorksheetIndex = 4; re.Cps = new List<ColumnIndexProperty>(){ new ColumnIndexProperty(){Index=1,PropertyName= "Serial"}, new ColumnIndexProperty(){Index=2,PropertyName= "Customer"}, new ColumnIndexProperty(){Index=3,PropertyName= "OrderNo"}, new ColumnIndexProperty(){Index=4,PropertyName= "MachineType"} , new ColumnIndexProperty(){Index=5,PropertyName= "ProductNO"} , new ColumnIndexProperty(){Index=6,PropertyName= "Quantity"} , new ColumnIndexProperty(){Index=8,PropertyName= "ReqDate"} }; re.NotNull = "1"; //配置文件导入方式 ReadExecl re = ReadExecl.GetReadExecl("TRP_CustomerOrder"); re.FilePath = fileName;