之前介紹過如何匯入/滙出Excel 檔案, 當嘗試開啟 CSV (Comma-separated Value) 檔案時, 因為File Header 與xls / xlsx 不符, 故NPOI 會彈出exception 如下:

與直接 CreateObject(“Excel.Application”) 不同, Excel 本身有處理CSV 檔案的機制, 而在.net 環境中, 其實可以直接當text file 讀取檔案, 並利用Array.Split(“,”) 配合迴圈來讀取資料. 但在這邊, 可以介紹一個Helper class CsvHelper 來簡化過程, 而且它還可以像ORM 進行DTO (Date-to-Object) mapping. 在示範中, 利用它作檔案匯入 / 匯出, 代碼如下:
介面 IWorkbookHelper.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lib.Util.WorkbookHelper
{
public interface IWorkbookHelper
{
void resetCurrentDataTable();
void addImportTableColumn(string colName, string colType);
void loadFile(string filePath);
DataTable readFileData(int sheetNum, int offsetLineNum);
void readFileHeader(int sheetNum, int headerline);
DataTable readFileToDataTable(string filePath, int sheetNum);
void exportDataToFile(DataTable dataTable, string fileName);
}
}
Base class WorkbookHelperBase.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lib.Util.WorkbookHelper
{
public abstract class WorkbookHelperBase: IWorkbookHelper
{
protected DataTable _dataTable;
protected String _filePath;
private string currentExcelFileType;
public WorkbookHelperBase()
{
resetCurrentDataTable();
}
public void resetCurrentDataTable()
{
_dataTable = new DataTable();
}
public abstract void loadFile(string filePath);
public abstract DataTable readFileData(int sheetNum, int offsetLineNum);
public abstract void readFileHeader(int sheetNum, int headerline);
public abstract void exportDataToFile(DataTable dataTable, string fileName);
public void addImportTableColumn(string colName, string colType)
{
switch (colType)
{
case TableColumnType.String:
_dataTable.Columns.Add(colName, typeof(string));
break;
case TableColumnType.Decimal:
_dataTable.Columns.Add(colName, typeof(decimal));
break;
default:
throw new Exception("Column type not support");
}
}
public DataTable readFileToDataTable(string filePath, int sheetNum)
{
this.loadFile(filePath);
this.resetCurrentDataTable();
this.readFileHeader(sheetNum, 0);
return this.readFileData(sheetNum, 1); //line 0 is header, data start from line 1
}
}
}
客制化的 CsvHelper class:
using CsvHelper;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lib.Util.WorkbookHelper
{
/// <summary>
/// Helper class to import / export CSV file content from / to DataTable.
/// </summary>
public class CSVHelper : WorkbookHelperBase
{
/// <summary>
/// Export Data to File.
/// </summary>
/// <param name="dataTable">Data source.</param>
/// <param name="fileName">Target file name.</param>
public override void exportDataToFile(DataTable dataTable, string fileName)
{
if (File.Exists(fileName))
throw new InvalidDataException("File " + fileName + " already existed.");
using (TextWriter textWriter = File.CreateText(fileName))
{
using (CsvWriter csvWriter = new CsvWriter(textWriter))
{
List<DataColumn> dataColumns = dataTable.Columns.Cast<DataColumn>().ToList();
// Write header to csv file.
dataColumns.ForEach(delegate (DataColumn dataColumn)
{
csvWriter.WriteField(dataColumn.ColumnName);
});
csvWriter.NextRecord();
// Write data to csv file.
dataTable.Rows.Cast<DataRow>().ToList().ForEach(delegate (DataRow dataRow)
{
dataColumns.ForEach(delegate (DataColumn dataColumn)
{
csvWriter.WriteField(dataColumn.DataType, dataRow[dataColumn.ColumnName]);
});
csvWriter.NextRecord();
});
}
}
}
/// <summary>
/// Load CSV File.
/// </summary>
/// <param name="filePath">File path.</param>
public override void loadFile(string filePath)
{
if (!File.Exists(filePath))
throw new InvalidDataException("Invalid filePath. Check File path is valid or accessable or not.");
if (!Path.GetExtension(filePath).ToUpper().Equals(ExcelFileType.csv.ToUpper()))
throw new InvalidDataException("Invalid file extension detected. Only allow CSV file.");
this._filePath = filePath;
}
/// <summary>
/// Read CSV File data.
/// </summary>
/// <param name="sheetNum">Sheet no. Default 0.</param>
/// <param name="offsetLineNum">Line of start reading data</param>
/// <returns></returns>
public override DataTable readFileData(int sheetNum=0, int offsetLineNum=1)
{
using (TextReader textReader = File.OpenText(_filePath))
{
using (CsvReader csvReader = new CsvReader(textReader))
{
// Initial CSV reader settings.
if (offsetLineNum == 0)
csvReader.Configuration.HasHeaderRecord = false;
// Initialize column if not do previously. Then get list of column name.
if (_dataTable.Columns.Count <= 0)
this.readFileHeader(0, 0);
List<DataColumn> dataColumns = _dataTable.Columns.Cast<DataColumn>().ToList();
// Get data.
while (csvReader.Read())
{
DataRow dataRow = _dataTable.NewRow();
dataColumns.ForEach(delegate (DataColumn dataColumn)
{
// Update dataRow if value found.
if(!String.IsNullOrEmpty(csvReader.GetField(dataColumn.ColumnName)))
dataRow[dataColumn] = csvReader.GetField(dataColumn.DataType, dataColumn.ColumnName);
});
_dataTable.Rows.Add(dataRow);
}
}
}
return _dataTable;
}
/// <summary>
/// Read Excel Header to Data Table.
/// </summary>
/// <param name="sheetNum">Sheet no. Default 0.</param>
/// <param name="headerline">Header line. Default 0.</param>
public override void readFileHeader(int sheetNum=0, int headerline=0)
{
using (TextReader textReader = File.OpenText(_filePath))
{
using (CsvReader csvReader = new CsvReader(textReader))
{
csvReader.Read();
csvReader.FieldHeaders.ToList().ForEach(delegate (String fieldHeader)
{
string fieldData = csvReader.GetField(fieldHeader);
_dataTable.Columns.Add(fieldHeader, (DataTypeUtil.ParseString(fieldData)));
});
}
}
}
}
}
Reference
Leave a Reply