1. 程式人生 > 其它 >.netcore匯入匯出Excel

.netcore匯入匯出Excel

使用NPOI包2.5.4版

思路:簡單的匯入匯出,就是一表、一行、一單元格讀寫的過程

讀取分三種常見情況,1.一次讀取全部表的全部資料2.讀取單表的所有資料3.按指定數量讀取

寫入分兩種情況,1.一次寫入全部資料2.按指定數量寫入

使用的時候只需要繼承匯入或者匯出類,並實現資料轉換方法,簡單的匯入匯出就OK了

匯出的時候用了新版NPOI快取新方法,不用在怕海量匯出時候記憶體爆炸,新問題是.....C盤要大不然臨時快取會讓C盤爆炸

  1     /// <summary>
  2     /// 需要處理Excel資料匯入,繼承本類並實現方法
  3     /// </summary>
4 public abstract class ExcelImport 5 { 6 ISheet _sheet = null; 7 IWorkbook _workbook = null; 8 int _numberOfSheets = 0; 9 int _sheetsIndexByCount = 0; 10 int _sheetsIndex = 0; 11 int _rowIndex = 0; 12 bool _skipFirst = false
; 13 public ExcelImport(string filePath, bool skipFirst = false) 14 { 15 var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); 16 if (filePath.IndexOf(".xlsx") > 0) // 2007版本 17 _workbook = new XSSFWorkbook(fs); 18 else
if (filePath.IndexOf(".xls") > 0) // 2003版本 19 _workbook = new HSSFWorkbook(fs); 20 _numberOfSheets = _workbook.NumberOfSheets; 21 _skipFirst = skipFirst; 22 } 23 /// <summary> 24 /// 處理一行轉成string集合的資料 25 /// 原始表格資料,需要重寫處理方法 26 /// 返回資料型別必須和讀取方法傳參的T型別一致 27 /// 可以用{ typeof(T) == typeof(模型類) }判斷,用於分別處理不同模型的資料 28 /// </summary> 29 protected abstract Object AnalysisData<T>(List<string> origin) where T : new(); 30 /// <summary> 31 /// 讀取自定義數量資料 32 /// 單表數量不足,會讀取下一張表 33 /// </summary> 34 public virtual List<T> ReadSheetByCount<T>(int readCount) where T : new() 35 { 36 List<T> data = new(); 37 if (_rowIndex == 0) _rowIndex = Convert.ToInt32(_skipFirst); 38 IRow dataRow; 39 int rowCount = 0;//最後一行的標號(標號從0開始) 40 if (_sheet != null) rowCount = _sheet.LastRowNum + 1; 41 do 42 { 43 if (_sheet == null || rowCount == _rowIndex) 44 { 45 if (_sheetsIndexByCount == _numberOfSheets) break; 46 _sheet = _workbook.GetSheetAt(_sheetsIndexByCount); 47 _sheetsIndexByCount++; 48 _rowIndex = Convert.ToInt32(_skipFirst); 49 rowCount = _sheet.LastRowNum + 1; 50 } 51 52 dataRow = _sheet.GetRow(_rowIndex); 53 _rowIndex++; 54 if (dataRow == null) continue; //沒有資料的行預設是null 55 var origin = dataRow.Cells.Select(m => m.StringCellValue.ToTrim()).ToList(); 56 if (origin.Where(m => string.IsNullOrEmpty(m)).Count() == origin.Count) continue; //整行資料都為空 57 data.Add((T)AnalysisData<T>(origin)); 58 } while (readCount > data.Count); 59 60 return data; 61 } 62 /// <summary> 63 /// 處理單表資料 64 /// return null表示沒有更多sheet可以讀取了 65 /// </summary> 66 public virtual List<T> ReadNextSheet<T>() where T : new() 67 { 68 List<T> data = new(); 69 if (_sheetsIndex < _numberOfSheets) 70 { 71 _sheet = _workbook.GetSheetAt(_sheetsIndex); 72 _sheetsIndex++; 73 } 74 else { return null; } 75 76 int rowIndex = Convert.ToInt32(_skipFirst); 77 IRow dataRow; 78 int rowCount = _sheet.LastRowNum + 1;//最後一行的標號(標號從0開始) 79 do 80 { 81 dataRow = _sheet.GetRow(rowIndex); 82 rowIndex++; 83 if (dataRow == null) continue; //沒有資料的行預設是null 84 var origin = dataRow.Cells.Select(m => m.StringCellValue.ToTrim()).ToList(); 85 if (origin.Where(m => string.IsNullOrEmpty(m)).Count() == origin.Count) continue; //整行資料都為空 86 data.Add((T)AnalysisData<T>(origin)); 87 } while (rowIndex < rowCount); 88 return data; 89 } 90 /// <summary> 91 /// 一次性處理全部sheet資料 92 /// *不建議,過量資料可能導致記憶體溢位 93 /// </summary> 94 public virtual List<T> ReadAllSheet<T>() where T : new() 95 { 96 List<T> data = new(); 97 do 98 { 99 data.AddRange(ReadNextSheet<T>()); 100 } while (_sheetsIndex < _numberOfSheets); 101 return data; 102 } 103 } 104 /// <summary> 105 /// 需要處理Excel資料匯出,繼承本類並實現方法 106 /// </summary> 107 public abstract class ExcelExport 108 { 109 SXSSFWorkbook _workbook;// 虛擬工作薄 110 SXSSFSheet _sheet;// 虛擬工作表 111 IRow _row; 112 ICell _newCell; 113 int _rowIndex = 0; 114 int _rowMax = 1024 * 1024;//預設單表最大行數 115 string _filePath; 116 117 ICellStyle Title { get; set; } 118 ICellStyle Cell { get; set; } 119 120 /// <summary> 121 /// 匯出Excel 122 /// </summary> 123 /// <param name="filePath">儲存檔案路徑(不帶檔名)</param> 124 /// <param name="maxRowCount">單表最大資料行,預設1W條</param> 125 public ExcelExport(string directory, int maxRowCount = 10000) 126 { 127 _filePath = directory + "\\" + Guid.NewGuid().ToString("N").ToLower() + ".xlsx"; 128 if (!Directory.Exists(directory)) Directory.CreateDirectory(directory); 129 if (System.IO.File.Exists(_filePath)) System.IO.File.Delete(_filePath); 130        // 需要先建立一個xlsx檔案不然後面會報格式錯誤 131 using (FileStream fs = new FileStream(_filePath, FileMode.Create, FileAccess.ReadWrite)) 132 { 133 var wb = new XSSFWorkbook(); 134 wb.Write(fs); 135 wb.Close(); 136 } 137 138 _workbook = new SXSSFWorkbook(new XSSFWorkbook(), 500);// 建立一個xlsx格式的虛擬表,500表示每500條資料寫入硬碟快取,釋放記憶體 139 140 if (maxRowCount <= _rowMax) _rowMax = maxRowCount; 141 142 Title = _workbook.CreateCellStyle(); 143 Title.Alignment = HorizontalAlignment.Center; 144 Title.VerticalAlignment = VerticalAlignment.Center; 145 IFont font = _workbook.CreateFont(); 146 font.Color = IndexedColors.Red.Index; 147 Title.SetFont(font); 148 149 Cell = _workbook.CreateCellStyle(); 150 Cell.Alignment = HorizontalAlignment.Justify;//兩端自動對齊(自動換行) 151 Cell.VerticalAlignment = VerticalAlignment.Center; 152 } 153 /// <summary> 154 /// 處理一個實體物件 155 /// 將實體物件資料轉換成匯入Excel用得資料結構 156 /// 可以用{ typeof(T) == typeof(模型類) }判斷 157 /// </summary> 158 protected abstract List<ExcelCell> ConvertData<T>(T origin) where T : new(); 159 /// <summary> 160 /// 寫入資料(在虛擬工作薄中操作) 161 /// 單表寫滿,會建立新表 162 /// 如果出現轉換失敗的資料會出現在返回的List<T>集合中 163 /// </summary> 164 public virtual List<T> WriteByCount<T>(List<T> data, List<string> title = null) where T : new() 165 { 166 if (data == null && data.Count == 0) 167 return default; 168 169 List<T> error = new(); 170 int columnNum = 0, dataIndex = 0; 171 if (title == null) columnNum = ConvertData<T>(data[0]).Count; 172 else columnNum = title.Count; 173 174 do 175 { 176 if (_rowIndex == 0 || _rowIndex == _rowMax) 177 { 178 _sheet = _workbook.CreateSheetByAutoWidth(columnNum); 179 _rowIndex = 0; 180 if (title != null) 181 { 182 _row = _sheet.CreateRowByHeight(_rowIndex); 183 _rowIndex++; 184 for (int j = 0; j < title.Count; j++) 185 { 186 _newCell = _row.CreateCell(j); 187 _newCell.SetCellValue(title[j]); 188 _newCell.CellStyle = Title; 189 } 190 } 191 } 192 193 _row = _sheet.CreateRowByHeight(_rowIndex); 194 try 195 { 196 var cellData = ConvertData<T>(data[dataIndex]); 197 for (int i = 0; i < cellData.Count; i++) 198 { 199 _newCell = _row.CreateCell(i); 200 _newCell.CellStyle = Cell; 201 switch (cellData[i].CellType) 202 { 203 case ExcelCellType.Boolean: 204 _newCell.SetCellValue(bool.Parse(cellData[i].Value)); 205 break; 206 case ExcelCellType.DateTime: 207 _newCell.SetCellValue(DateTime.Parse(cellData[i].Value)); 208 break; 209 case ExcelCellType.Number: 210 _newCell.SetCellValue(double.Parse(cellData[i].Value)); 211 break; 212 case ExcelCellType.String: 213 _newCell.SetCellValue(cellData[i].Value); 214 break; 215 default: 216 _newCell.SetCellValue(cellData[i].Value); 217 break; 218 } 219 } 220 _rowIndex++; 221 } 222 catch 223 { 224 error.Add(data[dataIndex]); 225 } 226 finally 227 { 228 dataIndex++; 229 } 230 } while (data.Count > dataIndex); 231 return error; 232 } 233 /// <summary> 234 /// 單表寫入資料(在虛擬工作薄中操作) 235 /// 資料總數超過單表最大的行數剩下部分不會寫入 236 /// 如果出現轉換失敗的資料會出現在返回的List<T>集合中 237 /// </summary> 238 public virtual List<T> WriteBySheet<T>(List<T> data, List<string> title = null) where T : new() 239 { 240 if (data == null && data.Count == 0) 241 return default; 242 243 List<T> error = new(); 244 int columnNum = 0, dataIndex = 0, rowIndex = 0; 245 246 if (title == null) columnNum = ConvertData<T>(data[0]).Count; 247 else columnNum = title.Count; 248 249 _sheet = _workbook.CreateSheetByAutoWidth(columnNum); 250 if (title != null) 251 { 252 _row = _sheet.CreateRowByHeight(rowIndex); 253 rowIndex++; 254 for (int i = 0; i < title.Count; i++) 255 { 256 _newCell = _row.CreateCell(i); 257 _newCell.SetCellValue(title[i]); 258 _newCell.CellStyle = Title; 259 } 260 } 261 262 do 263 { 264 if (rowIndex == 1024 * 1024) break;// 超出單表最大行數 265 _row = _sheet.CreateRowByHeight(rowIndex); 266 try 267 { 268 var cellData = ConvertData<T>(data[dataIndex]); 269 for (int i = 0; i < cellData.Count; i++) 270 { 271 _newCell = _row.CreateCell(i); 272 _newCell.CellStyle = Cell; 273 switch (cellData[i].CellType) 274 { 275 case ExcelCellType.Boolean: 276 _newCell.SetCellValue(bool.Parse(cellData[i].Value)); 277 break; 278 case ExcelCellType.DateTime: 279 _newCell.SetCellValue(DateTime.Parse(cellData[i].Value)); 280 break; 281 case ExcelCellType.Number: 282 _newCell.SetCellValue(double.Parse(cellData[i].Value)); 283 break; 284 case ExcelCellType.String: 285 _newCell.SetCellValue(cellData[i].Value); 286 break; 287 default: 288 _newCell.SetCellValue(cellData[i].Value); 289 break; 290 } 291 } 292 rowIndex++; 293 } 294 catch { error.Add(data[dataIndex]); } 295 finally { dataIndex++; } 296 } while (data.Count > dataIndex); 297 return error; 298 } 299 /// <summary> 300 /// 完成寫入獲取匯出檔案路徑,把硬碟上的臨時檔案資料打包成xlsx檔案 301 /// </summary> 302 public virtual string WriteEnd() 303 { 304 using (FileStream fs = File.Open(_filePath, FileMode.Open)) 305 { 306 _workbook.Write(fs); 307 _workbook.Dispose(); 308 return _filePath; 309 } 310 } 311 } 312 /// <summary> 313 /// 擴充套件 314 /// </summary> 315 public static class ISheetExt 316 { 317 public static IRow CreateRowByHeight(this ISheet sheet, int rownum) 318 { 319 var row = sheet.CreateRow(rownum); 320 row.HeightInPoints = 30; 321 return row; 322 } 323 public static SXSSFSheet CreateSheetByWidth(this IWorkbook workbook, int columnnum, int width) 324 { 325 width = width * 256;// Excel表格寬度單位跟畫素不一樣 326 var sheet = (SXSSFSheet)workbook.CreateSheet(); 327 for (int i = 0; i < columnnum; i++) 328 sheet.SetColumnWidth(i, width); 329 return sheet; 330 } 331 public static SXSSFSheet CreateSheetByAutoWidth(this IWorkbook workbook, int columnnum) 332 { 333 var sheet = (SXSSFSheet)workbook.CreateSheet(); 334 for (int i = 0; i < columnnum; i++) 335 { 336 sheet.TrackColumnForAutoSizing(i); 337 sheet.AutoSizeColumn(i); 338 } 339 return sheet; 340 } 341 } 342 public class ExcelCell 343 { 344 public ExcelCellType CellType { get; set; } 345 public string Value { get; set; } 346 } 347 public enum ExcelCellType 348 { 349 Boolean, 350 DateTime, 351 Number, 352 String 353 }