前面咱們已經實現了反射機制進行excel表格數據的解析,既然有上傳就得有下載,咱們再來寫一個通用的導出方法,利用反射機制實現對系統全部數據列表的篩選結果導出excel功能。node
咱們來構想一下這樣一個畫面,管理員篩選出北京的全部員工數據,想導出成excel表格;管理員篩選出北京全部欠費的企業數據,想導出成excel表格;管理員想導出本月的工單報表到excel表格;管理員想導出近3月北京各崗位的運營報表到excel表格......工具
系統客服真是個神奇的職業,神馬都想導出到excel表格!字體
那麼對於咱們的程序來講,該怎麼作呢!因爲每一個導出功能對應的數據源都不同,簡單的方法很難作到,只能每一個業務單寫一個導出功能。spa
因此,咱們再次沿用導入時使用的xml文件配置作橋樑,經過反射技術作具體實現的方法,作一個通用的EXCEL導出工具:傳入DTO數據集合與xml規則集,返回excel文件流。excel
1.咱們建立一個xml文件,裏面存放全部須要導出功能涉及字段的規則集(博主項目涉及到的導出功能也就10來個列表,約涉及100多個字段(這兒未所有列出),故未分開成多個xml規則文件)code
1 <?xml version="1.0" encoding="utf-8" ?> 2 <module> 3 <add ExportFieldName="姓名" PropertyName="Name" DataType="System.String"/> 4 <add ExportFieldName="手機號碼" PropertyName="PhoneNumber" DataType="System.String"/> 5 <add ExportFieldName="性別" PropertyName="Sex" DataType="System.String"/> 6 <add ExportFieldName="民族" PropertyName="Nation" DataType="System.String"/> 7 <add ExportFieldName="出生日期" PropertyName="Birthday" DataType="System.String"/> 8 <add ExportFieldName="身份證號碼" PropertyName="Cardid" DataType="System.String"/> 9 </module>
注:xml
1.導出的規則相對簡單,因此咱們建立一個簡單的規則集類:對象
1 /// <summary> 2 /// 導出excel-中英文規則類 3 /// </summary> 4 public class ExportRegular 5 { 6 /// <summary> 7 /// 屬性名稱(英文) 8 /// </summary> 9 public string PropertyName { get; set; } 10 11 /// <summary> 12 /// 數據類型 13 /// </summary> 14 public string DataType { get; set; } 15 16 /// <summary> 17 /// 導出名稱(中文) 18 /// </summary> 19 public string ExportFieldName { get; set; } 20 }
2.而後是解析XML規則集的方法blog
1 /// <summary> 2 /// 解析XML規則集文件 3 /// </summary> 4 /// <returns></returns> 5 public static List<ExportRegular> GetExportRegulars() 6 { 7 var result = new List<ExportRegular>(); 8 9 var reader = new XmlTextReader(xmlpath); 10 var doc = new XmlDocument(); 11 //從指定的XMLReader加載XML文檔 12 doc.Load(reader); 13 14 foreach (XmlNode node in doc.DocumentElement.ChildNodes) 15 { 16 var header = new ExportRegular(); 17 18 if (node.Attributes["PropertyName"] != null) 19 header.PropertyName = node.Attributes["PropertyName"].Value; 20 if (node.Attributes["DataType"] != null) 21 header.DataType = node.Attributes["DataType"].Value; 22 if (node.Attributes["ExportFieldName"] != null) 23 header.ExportFieldName = node.Attributes["ExportFieldName"].Value; 24 25 result.Add(header); 26 } 27 28 return result; 29 }
2.咱們的excel導出工具對外暴露兩個靜態方法:接口
1 public static MemoryStream CreateExcelStreamByDatas(List<object> objectDatas, KeyValuePair<string, string> excelHeader) 2 3 public static MemoryStream CreateExcelStreamByDatas(List<KeyValuePair<List<object>, KeyValuePair<string, string>>> objectDatass)
一個是單sheet表單導出接口,一個是多表單導出接口(例:管理員導出東三省的員工數據,則導出excel含4個表單(一、東三省所有數據;二、遼寧省數據;三、吉林省數據;四、黑龍江省數據))
參數objectDatas是從庫中篩選到的結果DTO數據集,excelHeader是一個鍵值對:key-表頭名稱,value-sheet表單名稱
3.咱們具體來看單表單方法的實現:
1 /// <summary> 2 /// 將數據轉換成excel文件流輸出 ->單表單導出接口 3 /// </summary> 4 /// <returns></returns> 5 public static MemoryStream CreateExcelStreamByDatas(List<object> objectDatas, KeyValuePair<string, string> excelHeader) 6 { 7 // 返回對象 8 var ms = new MemoryStream(); 9 10 // excel工做簿 11 IWorkbook workbook = new HSSFWorkbook(); 12 //導入數據到sheet表單 13 CreateExcelSheetByDatas(objectDatas, excelHeader.Key, excelHeader.Value, ref workbook); 14 15 workbook.Write(ms); 16 ms.Flush(); 17 ms.Position = 0; 18 19 return ms; 20 }
咱們將具體功能邏輯抽象到了方法CreateExcelSheetByDatas中,
那麼多表單的實現只須要修改第13行爲:
1 foreach (KeyValuePair<List<object>, KeyValuePair<string, string>> keyValuePair in objectDatass) 2 { 3 //導入數據到sheet表單 4 CreateExcelSheetByDatas(keyValuePair.Key, keyValuePair.Value.Key, keyValuePair.Value.Value, ref workbook); 5 }
4.咱們來實現最核心的方法CreateExcelSheetByDatas(博主還沒來得及重構代碼,全部邏輯都寫在裏面了,略長...)
1 /// <summary> 2 /// 根據傳入數據新建sheet表單到指定workbook 3 /// </summary> 4 /// <param name="objectDatas"></param> 5 /// <param name="excelHeader"></param> 6 /// <param name="sheetName"></param> 7 /// <param name="regulars"></param> 8 /// <param name="workbook"></param> 9 private static void CreateExcelSheetByDatas(List<object> objectDatas, string excelHeader, string sheetName, ref IWorkbook workbook) 10 { 11 var regulars = GetExportRegulars(); 12 13 // excel sheet表單 14 ISheet sheet = workbook.CreateSheet(sheetName); 15 // excel行數 16 int rows = 0; 17 18 #region 單元格 -表頭格式 19 20 #region 表頭字體 21 22 IFont fontTitle = workbook.CreateFont(); 23 fontTitle.FontHeightInPoints = 12; 24 fontTitle.Boldweight = (short)FontBoldWeight.BOLD; 25 26 #endregion 27 28 ICellStyle styleTitle = workbook.CreateCellStyle(); 29 styleTitle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.CENTER; 30 styleTitle.SetFont(fontTitle); 31 styleTitle.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.CENTER; 32 33 #endregion 34 35 #region 單元格 -表體格式 36 37 #region 表體字體 38 39 IFont fontMessage = workbook.CreateFont(); 40 fontMessage.FontHeightInPoints = 10; 41 42 #endregion 43 44 ICellStyle styleMessage = workbook.CreateCellStyle(); 45 styleMessage.Alignment = NPOI.SS.UserModel.HorizontalAlignment.CENTER; 46 styleMessage.SetFont(fontMessage); 47 styleMessage.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.CENTER; 48 49 #endregion 50 51 // 建立表頭並賦值 52 int firstRowCellCount = GetAttributeCount(objectDatas.First()); 53 IRow headerRow = sheet.CreateRow(rows); 54 headerRow.HeightInPoints = 40; 55 var headerCell = headerRow.CreateCell(0); 56 headerCell.SetCellValue(excelHeader); 57 58 // 合併表頭 59 var cellRangeAddress = new CellRangeAddress(rows, rows, 0, firstRowCellCount - 1); 60 sheet.AddMergedRegion(cellRangeAddress); 61 // 設置表頭格式 62 headerCell.CellStyle = styleTitle; 63 64 65 //生成表頭 66 if (objectDatas.Any()) 67 { 68 rows++; 69 // excel列數 70 int cells = -1; 71 // 建立數據行 72 var firstRow = sheet.CreateRow(rows); 73 firstRow.HeightInPoints = 16; 74 var objectData = objectDatas.FirstOrDefault(); 75 foreach (System.Reflection.PropertyInfo p in objectData.GetType().GetProperties()) 76 { 77 cells++; 78 var regular = regulars.Find(t => t.PropertyName == p.Name); 79 if (regular == null) 80 { 81 throw new Exception("導出excel時,出現未配置字段。表:" + objectData.GetType().Name + ",字段:" + p.Name); 82 } 83 var firstRowCell = firstRow.CreateCell(cells); 84 firstRowCell.SetCellValue(regular.ExportFieldName); 85 sheet.SetColumnWidth(cells, regular.ExportFieldName.Length * 256 * 4); 86 firstRowCell.CellStyle = styleMessage; 87 } 88 } 89 90 // 反射object對象,遍歷字段 91 foreach (var objectData in objectDatas) 92 { 93 rows++; 94 // excel列數 95 int cells = -1; 96 // 建立數據行 97 var messageRow = sheet.CreateRow(rows); 98 messageRow.HeightInPoints = 16; 99 foreach (System.Reflection.PropertyInfo p in objectData.GetType().GetProperties()) 100 { 101 cells++; 102 var regular = regulars.Find(t => t.PropertyName == p.Name); 103 var messageCell = messageRow.CreateCell(cells); 104 var value = p.GetValue(objectData); 105 if (value == null) 106 { 107 messageCell.SetCellValue(""); 108 } 109 else 110 { 111 switch (regular.DataType) 112 { 113 case "datetime": 114 if (Convert.ToDateTime(value) == DateTime.MinValue) 115 { 116 messageCell.SetCellValue(""); 117 } 118 else 119 { 120 messageCell.SetCellValue( 121 Convert.ToDateTime(value).ToString("yyyy-MM-dd HH:mm:ss")); 122 } 123 break; 124 case "int": 125 messageCell.SetCellValue(Convert.ToInt32(value)); 126 break; 127 case "double": 128 messageCell.SetCellValue(Convert.ToDouble(value)); 129 break; 130 case "bool": 131 var setValue = "是"; 132 if (!(bool)value) 133 { 134 setValue = "否"; 135 } 136 messageCell.SetCellValue(setValue); 137 break; 138 default: 139 messageCell.SetCellValue(value.ToString()); 140 break; 141 } 142 } 143 messageCell.CellStyle = styleMessage; 144 } 145 } 146 }
注:
1.第18到62行均是對錶體、表頭字體格式的指定,並建立合併表頭名稱
2.第66行到88行是在遍歷DTO屬性,按規則集取出對應中文名稱建立表頭字段
此處特別說明:博主把全部導出DTO的全部屬性字段都是作的簡單類型字段,方便作反射。
3.第52行GetAttributeCount方法,是獲取DTO對象全部屬性個數
4.第91行到145行是按規則集規則將DTO中對應數據寫進excel單元格
至此,咱們就實現了將DTO集合按規則集導出到excel表格的方法。
咱們只須要在各個導出服務中,先查庫獲取到須要的數據集合,再Map到DTO集合中,做爲參數調用EXCEL導出方法便可返回須要的excel文件流。
到這兒,NPOI操做簡單模板excel進行導入導出的相關代碼就貼完了。若有什麼地方寫的不對或很差,歡迎指出,必定虛心請教~~~
上一篇博文說到的那些反人類的excel的導入,會在下一篇博文貼出具體實現的代碼,敬請期待~~~
原創文章,代碼都是從本身項目裏貼出來的。轉載請註明出處哦,親~~~