前言
时间过得好快,在之前升级到3.0之后,就感觉好久没再动过啥东西了,之前有问到Swagger的中文汉化,虽说我觉得这种操作的意义不是太大,也是多少鼓捣了下,其实个人感觉就是元素内容替换,既然可以执行js了那不就是网页上随便搞了,所以就没往下再折腾,但是现在需要用到Excel的操作了,那就不得不提起这个NPOI了。
NPOI
在之前.net framework的时候,工程需要用到Excel的导入导出,当然用这个NPOI是偶然,也就是找了下这个看着可以就用了,之后遇到的各种问题也都找资料解决,什么多行合并啊,打开2007版本错误啊之类的,但是不得不说,用着还挺好,所以既然net core需要了,那就看看呗,刚好也是支持的。
在Util我们来引入这个类库NPOI。
在使用之前,我们先缕一下获取Excel数据需要哪些准备操作。
- 获取文件(这个就不多说)
- 获取sheet信息(考虑有可能多sheet操作)
- 根据sheet获取对应文件信息(多少行,当然有些还有合并)
- 根据合并行来判断第一行是否为标题
- 判断哪一行是列名(用于对应数据)
- 遍历每一行并根据每一行的数据格式来获取(有可能是公式/日期/数字/普通文本等等)
ok,大致上清楚了之后,我们就一步步来看吧,这里我创建一个ExcelUtil,来写第一个方法(这里只做说明展示吧)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
| public class ExcelUtil { public static DataSet ReadExcelToDataSet(string filePath, string sheetName = null) { if (!File.Exists(filePath)) { LogUtil.Debug($"未找到文件{filePath}"); return null; } FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); IWorkbook workbook = WorkbookFactory.Create(fs); ISheet sheet = null; DataSet ds = new DataSet(); if (!string.IsNullOrEmpty(sheetName)) { sheet = workbook.GetSheet(sheetName); if (sheet == null) { LogUtil.Debug($"{filePath}未找到sheet:{sheetName}"); return null; } DataTable dt = ReadExcelFunc(workbook, sheet); ds.Tables.Add(dt); } else { int sheetCount = workbook.NumberOfSheets; for (int i = 0; i < sheetCount; i++) { sheet = workbook.GetSheetAt(i); if (sheet != null) { DataTable dt = ReadExcelFunc(workbook, sheet); ds.Tables.Add(dt); } } } return ds; }
private static DataTable ReadExcelFunc(IWorkbook workbook, ISheet sheet) { DataTable dt = new DataTable(); IRow cells = sheet.GetRow(sheet.FirstRowNum); int cellsCount = cells.PhysicalNumberOfCells; int emptyCount = 0; int cellIndex = sheet.FirstRowNum; List<string> listColumns = new List<string>(); bool isFindColumn = false; while (!isFindColumn) { emptyCount = 0; listColumns.Clear(); for (int i = 0; i < cellsCount; i++) { if (string.IsNullOrEmpty(cells.GetCell(i).StringCellValue)) { emptyCount++; } listColumns.Add(cells.GetCell(i).StringCellValue); } if (emptyCount == 0) { isFindColumn = true; } cellIndex++; cells = sheet.GetRow(cellIndex); }
foreach (string columnName in listColumns) { if (dt.Columns.Contains(columnName)) { continue; } dt.Columns.Add(columnName, typeof(string)); } int rowsCount = sheet.PhysicalNumberOfRows; cellIndex += 1; DataRow dr = null; for (int i = cellIndex; i < rowsCount; i++) { cells = sheet.GetRow(i); dr = dt.NewRow(); for (int j = 0; j < dt.Columns.Count; j++) { switch (cells.GetCell(j).CellType) { case CellType.String: dr[j] = cells.GetCell(j).StringCellValue; break; case CellType.Numeric: dr[j] = cells.GetCell(j).NumericCellValue.ToString(); break; case CellType.Unknown: dr[j] = cells.GetCell(j).StringCellValue; break; } } dt.Rows.Add(dr); } return dt; } }
|
文件的导入操作就不再演示了,之前有文件上传的相关操作方法net core WebApi——文件分片上传与跨域请求处理。
导入的处理这里也只是大致演示下,具体需要的东西包括情况可能会比较复杂,但是终归数据还是那些,只是操作方法不同罢了(别说什么骚操作)。
相对于导入,导出的流程就比较简单了。
- 获取数据信息(sql或文件)
- 组成数据集合(List或DataTable)
- 创建sheet
- 设置相关样式等等
- 遍历赋值row
- 导出文件流
了解完,我们就继续来搞吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| public static byte[] ExportExcel<T>(List<T> entities,Dictionary<string,string> dicColumns, string title = null) { if (entities.Count <= 0) { return null; } IWorkbook workbook = new XSSFWorkbook(); ISheet sheet = workbook.CreateSheet("test"); IRow cellsColumn = null; IRow cellsData = null; PropertyInfo[] properties = entities[0].GetType().GetProperties(); int cellsIndex = 0; if (!string.IsNullOrEmpty(title)) { ICellStyle style = workbook.CreateCellStyle(); style.BorderBottom = BorderStyle.Dotted; style.BorderLeft = BorderStyle.Hair; style.BorderRight = BorderStyle.Hair; style.BorderTop = BorderStyle.Dotted; style.Alignment = HorizontalAlignment.Left;
style.VerticalAlignment = VerticalAlignment.Center;
IFont font = workbook.CreateFont(); font.FontHeightInPoints = 10; font.FontName = "微软雅黑"; style.SetFont(font);
IRow cellsTitle = sheet.CreateRow(0); cellsTitle.CreateCell(0).SetCellValue(title); cellsTitle.RowStyle = style; sheet.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(0, 1, 0, dicColumns.Count - 1)); cellsIndex = 2; } cellsColumn = sheet.CreateRow(cellsIndex); int index = 0; Dictionary<string, int> columns = new Dictionary<string, int>(); foreach (var item in dicColumns) { cellsColumn.CreateCell(index).SetCellValue(item.Value); columns.Add(item.Value, index); index++; } cellsIndex += 1; foreach (var item in entities) { cellsData = sheet.CreateRow(cellsIndex); for (int i = 0; i < properties.Length; i++) { if (!dicColumns.ContainsKey(properties[i].Name)) continue; object[] entityValues = new object[properties.Length]; entityValues[i] = properties[i].GetValue(item); index = columns[dicColumns[properties[i].Name]]; cellsData.CreateCell(index).SetCellValue(entityValues[i].ToString()); } cellsIndex++; }
byte[] buffer = null; using (MemoryStream ms = new MemoryStream()) { workbook.Write(ms); buffer = ms.GetBuffer(); ms.Close(); }
return buffer; }
|
测试
写完,免不了一通测试,这里不多说了,直接上图。
导入这里前面也说了没做界面上传什么的,就是一个文件路径,直接执行,Excel原文件我也会同步上传到代码仓库。
导出的话,这里也是用Swagger神器来测试。
数据库数据如下图。
带标题导出。
小结
最近真的是有点儿忙,一直在鼓捣opengl这类图形化的东西,各种矩阵转换模型转换,要么是用c++,qt写opengl,要么是用threejs搞opengl,唉,整的最近也只能是晚上回去摸索会儿net core,工作总是不那么尽如人意,但是身为程序猿的我们,不都是不断的摸索前进么?我们可以不会,但那不是我们不整的借口。