對於EXCEL的導入、導出,我之前已分享過多次,比如:
第一種方案:《我寫的一個ExcelHelper通用類,可用於讀取或生成數據》這個主要是利用把EXCEL當成一個DB來進行獲取數據,導出則是采用拼接成HTML TABLE的方式,這種在ASP.NET,ASP.NET CORE 中也是可以的,簡單方便,依賴Office Excel組件,僅適用於網站服務端。 推薦指數:♥♥♥
第二種方案:《MVC導出數據到EXCEL新方法:將視圖或分部視圖轉換為HTML后再直接返回FileResult》這個主要是實現導出EXCEL的方法,依賴MVC框架,利用視圖引擎解析渲染View(view也主要是HTML表格模板)獲得HTML TABLE,本質與第一種的導出相同,只是這里不用代碼去拼接HTML TABLE而是寫View HTML模板即可,簡單方便,適用於ASP.NET MVC、ASP.NET MVC CORE; 推薦指數:♥♥♥♥
第三種方案:《NPOI導入導出EXCEL通用類,供參考,可直接使用在WinForm項目中》這個實現了EXCEL的導入、導出方法,依賴NPOI,優點是:無需安裝EXCEL也能操作EXCEL,文中的實現方案及示例主要是用在C/S中,對B/S 支持不夠友好,當然可以進行適當修改就能兼容WEB版。推薦指數:♥♥♥♥
第四種方案:《分享我基於NPOI+ExcelReport實現的導入與導出EXCEL類庫》這個完美的靈活實現了EXCEL的各種導入、導出方法,支持基於模板導出,格式自定義等 特點,依賴:ExcelReport、NPOI、NPOI.Extend,支持C/S、B/S(C/S特有的方法除外),適用於企業級EXCEL操作比較復雜的場景:ExcelUtility,推薦指數:♥♥♥♥♥,git地址:https://gitee.com/zuowj/ExcelUtility
第五種方案:就是本文即將介紹的,依賴NPOI,實現EXCEL的導入、導出、上傳等常見方法,充分借鑒第三種、第四種方案,抽出核心的實現方法,並進行適當改造,以滿足絕大部份的EXCEL場景,輕量、簡單、易用,支持鏈式操作,類庫名:ExcelEasyUtil,故名思義:EXCEL簡單實用工具類庫。
該類庫主要是基於NPOI的IWorkbook進行擴展,擴展方法實現了常見的幾種導出EXCEL、導入EXCEL數據的方法:基於類型集合,基於DataTable,NPOIExtensions完整實現代碼如下:
using Newtonsoft.Json.Linq;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq.Expressions;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
namespace ExcelEasyUtil
{
/// <summary>
/// NPOI擴展類
/// author:zuowenjun
/// 2019-5-21
/// </summary>
public static class NPOIExtensions
{
/// <summary>
/// 將一個實體數據對象填充到一個EXCEL工作表中(可連續填充多個sheet,如:FillSheet(...).FillSheet(..) )
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="book"></param>
/// <param name="sheetName"></param>
/// <param name="headerColNames"></param>
/// <param name="excelData"></param>
/// <param name="getCellValueFunc"></param>
/// <returns></returns>
public static IWorkbook FillSheet<T>(this IWorkbook book, string sheetName, IList<T> excelData,
IList<string> headerColNames, Func<T, List<object>> getCellValuesFunc, IDictionary<string, string> colDataFormats = null) where T : class
{
var sheet = book.CreateSheet(sheetName);
IRow rowHeader = sheet.CreateRow(0);
var headerCellStyle = GetCellStyle(book, true);
Dictionary<int, ICellStyle> colStyles = new Dictionary<int, ICellStyle>();
List<Type> colTypes = new List<Type>();
Type strType = typeof(string);
for (int i = 0; i < headerColNames.Count; i++)
{
ICell headerCell = rowHeader.CreateCell(i);
headerCell.CellStyle = headerCellStyle;
string colName = headerColNames[i];
if (colName.Contains(":"))
{
var colInfos = colName.Split(':');
colName = colInfos[0];
colTypes.Add(GetColType(colInfos[1]));
}
else
{
colTypes.Add(strType);
}
headerCell.SetCellValue(colName);
if (colDataFormats != null && colDataFormats.ContainsKey(colName))
{
colStyles[i] = GetCellStyleWithDataFormat(book, colDataFormats[colName]);
}
else
{
colStyles[i] = GetCellStyle(book);
}
}
//生成excel內容
for (int i = 0; i < excelData.Count; i++)
{
IRow irow = sheet.CreateRow(i + 1);
var row = excelData[i];
var cellValues = getCellValuesFunc(row);
for (int j = 0; j < headerColNames.Count; j++)
{
ICell cell = irow.CreateCell(j);
string cellValue = string.Empty;
if (cellValues.Count - 1 >= j && cellValues[j] != null)
{
cellValue = cellValues[j].ToString();
}
SetCellValue(cell, cellValue, colTypes[j], colStyles);
}
}
return book;
}
/// <summary>
/// 將一個實體數據對象填充到一個EXCEL工作表中(可連續填充多個sheet,如:FillSheet(...).FillSheet(..) )
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="book"></param>
/// <param name="sheetName"></param>
/// <param name="colMaps"></param>
/// <param name="excelData"></param>
/// <returns></returns>
public static IWorkbook FillSheet<T>(this IWorkbook book, string sheetName, IList<T> excelData,
IDictionary<string, Expression<Func<T, dynamic>>> colMaps, IDictionary<string, string> colDataFormats = null
) where T : class
{
var sheet = book.CreateSheet(sheetName);
var headerColNames = new List<string>();
var propInfos = new List<PropertyInfo>();
foreach (var item in colMaps)
{
headerColNames.Add(item.Key);
var propInfo = GetPropertyInfo(item.Value);
propInfos.Add(propInfo);
}
var headerCellStyle = GetCellStyle(book, true);
Dictionary<int, ICellStyle> colStyles = new Dictionary<int, ICellStyle>();
IRow rowHeader = sheet.CreateRow(0);
for (int i = 0; i < headerColNames.Count; i++)
{
ICell headerCell = rowHeader.CreateCell(i);
headerCell.CellStyle = headerCellStyle;
headerCell.SetCellValue(headerColNames[i]);
if (colDataFormats != null && colDataFormats.ContainsKey(headerColNames[i]))
{
colStyles[i] = GetCellStyleWithDataFormat(book, colDataFormats[headerColNames[i]]);
}
else
{
colStyles[i] = GetCellStyle(book);
}
}
//生成excel內容
for (int i = 0; i < excelData.Count; i++)
{
IRow irow = sheet.CreateRow(i + 1);
var row = excelData[i];
for (int j = 0; j < headerColNames.Count; j++)
{
var prop = propInfos[j];
ICell cell = irow.CreateCell(j);
string cellValue = Convert.ToString(propInfos[j].GetValue(row, null));
SetCellValue(cell, cellValue, prop.PropertyType, colStyles);
}
}
return book;
}
/// <summary>
/// 將一個數據表(DataTable)對象填充到一個EXCEL工作表中(可連續填充多個sheet,如:FillSheet(...).FillSheet(..) )
/// </summary>
/// <param name="book"></param>
/// <param name="sheetName"></param>
/// <param name="excelData"></param>
/// <param name="colMaps"></param>
/// <param name="colDataFormats"></param>
/// <returns></returns>
public static IWorkbook FillSheet(this IWorkbook book, string sheetName, DataTable excelData, IDictionary<string, string> colMaps,
IDictionary<string, string> colDataFormats = null)
{
if (excelData.Rows.Count <= 0) return book;
var sheet = book.CreateSheet(sheetName);
var headerCellStyle = GetCellStyle(book, true);
Dictionary<int, ICellStyle> colStyles = new Dictionary<int, ICellStyle>();
IRow rowHeader = sheet.CreateRow(0);
int nIndex = 0;
var headerColNames = new List<string>();
foreach (var item in colMaps)
{
ICell headerCell = rowHeader.CreateCell(nIndex);
headerCell.SetCellValue(item.Value);
headerCell.CellStyle = headerCellStyle;
if (colDataFormats != null && colDataFormats.ContainsKey(item.Value))
{
colStyles[nIndex] = GetCellStyleWithDataFormat(book, colDataFormats[item.Value]);
}
else
{
colStyles[nIndex] = GetCellStyle(book);
}
headerColNames.Add(item.Key);
nIndex++;
}
//生成excel內容
for (int i = 0; i < excelData.Rows.Count; i++)
{
IRow irow = sheet.CreateRow(i + 1);
var row = excelData.Rows[i];
for (int j = 0; j < headerColNames.Count; j++)
{
ICell cell = irow.CreateCell(j);
string colName = headerColNames[j];
string cellValue = row[colName].ToNotNullString();
SetCellValue(cell, cellValue, excelData.Columns[colName].DataType, colStyles);
}
}
return book;
}
private static PropertyInfo GetPropertyInfo<T, TR>(Expression<Func<T, TR>> select)
{
var body = select.Body;
if (body.NodeType == ExpressionType.Convert)
{
var o = (body as UnaryExpression).Operand;
return (o as MemberExpression).Member as PropertyInfo;
}
else if (body.NodeType == ExpressionType.MemberAccess)
{
return (body as MemberExpression).Member as PropertyInfo;
}
return null;
}
private static Type GetColType(string colTypeSimpleName)
{
colTypeSimpleName = colTypeSimpleName.ToUpper();
switch (colTypeSimpleName)
{
case "DT":
{
return typeof(DateTime);
}
case "BL":
{
return typeof(Boolean);
}
case "NUM":
{
return typeof(Int64);
}
case "DEC":
{
return typeof(Decimal);
}
default:
{
return typeof(String);
}
}
}
private static void SetCellValue(ICell cell, string value, Type colType, IDictionary<int, ICellStyle> colStyles)
{
if (colType.IsNullableType())
{
colType = colType.GetGenericArguments()[0];
}
string dataFormatStr = null;
switch (colType.ToString())
{
case "System.String": //字符串類型
cell.SetCellType(CellType.String);
cell.SetCellValue(value);
break;
case "System.DateTime": //日期類型
DateTime dateV = new DateTime();
if (DateTime.TryParse(value, out dateV))
{
cell.SetCellValue(dateV);
}
else
{
cell.SetCellValue(value);
}
dataFormatStr = "yyyy/mm/dd hh:mm:ss";
break;
case "System.Boolean": //布爾型
bool boolV = false;
if (bool.TryParse(value, out boolV))
{
cell.SetCellType(CellType.Boolean);
cell.SetCellValue(boolV);
}
else
{
cell.SetCellValue(value);
}
break;
case "System.Int16": //整型
case "System.Int32":
case "System.Int64":
case "System.Byte":
int intV = 0;
if (int.TryParse(value, out intV))
{
cell.SetCellType(CellType.Numeric);
cell.SetCellValue(intV);
}
else
{
cell.SetCellValue(value);
}
dataFormatStr = "0";
break;
case "System.Decimal": //浮點型
case "System.Double":
double doubV = 0;
if (double.TryParse(value, out doubV))
{
cell.SetCellType(CellType.Numeric);
cell.SetCellValue(doubV);
}
else
{
cell.SetCellValue(value);
}
dataFormatStr = "0.00";
break;
case "System.DBNull": //空值處理
cell.SetCellType(CellType.Blank);
cell.SetCellValue(string.Empty);
break;
default:
cell.SetCellType(CellType.Unknown);
cell.SetCellValue(value);
break;
}
if (!string.IsNullOrEmpty(dataFormatStr) && colStyles[cell.ColumnIndex].DataFormat <= 0) //沒有設置,則采用默認類型格式
{
colStyles[cell.ColumnIndex] = GetCellStyleWithDataFormat(cell.Sheet.Workbook, dataFormatStr);
}
cell.CellStyle = colStyles[cell.ColumnIndex];
ReSizeColumnWidth(cell.Sheet, cell);
}
private static ICellStyle GetCellStyleWithDataFormat(IWorkbook workbook, string format)
{
ICellStyle style = GetCellStyle(workbook);
var dataFormat = workbook.CreateDataFormat();
short formatId = -1;
if (dataFormat is HSSFDataFormat)
{
formatId = HSSFDataFormat.GetBuiltinFormat(format);
}
if (formatId != -1)
{
style.DataFormat = formatId;
}
else
{
style.DataFormat = dataFormat.GetFormat(format);
}
return style;
}
private static ICellStyle GetCellStyle(IWorkbook workbook, bool isHeaderRow = false)
{
ICellStyle style = workbook.CreateCellStyle();
if (isHeaderRow)
{
style.FillPattern = FillPattern.SolidForeground;
style.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Grey25Percent.Index;
IFont f = workbook.CreateFont();
f.FontHeightInPoints = 11D;
f.Boldweight = (short)FontBoldWeight.Bold;
style.SetFont(f);
}
style.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin;
style.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin;
style.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;
style.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin;
return style;
}
private static void ReSizeColumnWidth(ISheet sheet, ICell cell)
{
int cellLength = (Encoding.Default.GetBytes(cell.ToString()).Length + 2) * 256;
const int maxLength = 60 * 256; //255 * 256;
if (cellLength > maxLength) //當單元格內容超過30個中文字符(英語60個字符)寬度,則強制換行
{
cellLength = maxLength;
cell.CellStyle.WrapText = true;
}
int colWidth = sheet.GetColumnWidth(cell.ColumnIndex);
if (colWidth < cellLength)
{
sheet.SetColumnWidth(cell.ColumnIndex, cellLength);
}
}
private static ISheet GetSheet(IWorkbook workbook, string sheetIndexOrName)
{
int sheetIndex = 0;
ISheet sheet = null;
if (int.TryParse(sheetIndexOrName, out sheetIndex))
{
sheet = workbook.GetSheetAt(sheetIndex);
}
else
{
sheet = workbook.GetSheet(sheetIndexOrName);
}
return sheet;
}
/// <summary>
/// 從工作表中解析生成DataTable
/// </summary>
/// <param name="workbook"></param>
/// <param name="sheetIndexOrName"></param>
/// <param name="headerRowIndex"></param>
/// <param name="startColIndex"></param>
/// <param name="colCount"></param>
/// <returns></returns>
public static DataTable ResolveDataTable(this IWorkbook workbook, string sheetIndexOrName, int headerRowIndex, short startColIndex = 0, short colCount = 0)
{
DataTable table = new DataTable();
ISheet sheet = GetSheet(workbook, sheetIndexOrName);
IRow headerRow = sheet.GetRow(headerRowIndex);
int cellFirstNum = (startColIndex > headerRow.FirstCellNum ? startColIndex : headerRow.FirstCellNum);
int cellCount = (colCount > 0 && colCount < headerRow.LastCellNum ? colCount : headerRow.LastCellNum);
for (int i = cellFirstNum; i < cellCount; i++)
{
if (headerRow.GetCell(i) == null || headerRow.GetCell(i).StringCellValue.Trim() == "")
{
// 如果遇到第一個空列,則不再繼續向后讀取
cellCount = i;
break;
}
DataColumn column = new DataColumn(headerRow.GetCell(i).StringCellValue);
table.Columns.Add(column);
}
for (int i = (headerRowIndex + 1); i <= sheet.LastRowNum; i++)
{
IRow row = sheet.GetRow(i);
if (row != null)
{
List<string> cellValues = new List<string>();
for (int j = cellFirstNum; j < cellCount; j++)
{
if (row.GetCell(j) != null)
{
cellValues.Add(row.GetCell(j).ToNotNullString());
}
else
{
cellValues.Add(string.Empty);
}
}
table.Rows.Add(cellValues.ToArray());
}
}
return table;
}
/// <summary>
/// 從工作表中解析生成指定的結果對象列表
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="workbook"></param>
/// <param name="sheetIndexOrName"></param>
/// <param name="headerRowIndex"></param>
/// <param name="buildResultItemFunc"></param>
/// <param name="startColIndex"></param>
/// <param name="colCount"></param>
/// <returns></returns>
public static List<T> ResolveAs<T>(this IWorkbook workbook, string sheetIndexOrName, int headerRowIndex, Func<List<string>, T> buildResultItemFunc,
short startColIndex = 0, short colCount = 0)
{
ISheet sheet = GetSheet(workbook, sheetIndexOrName);
IRow headerRow = sheet.GetRow(headerRowIndex);
int cellFirstNum = (startColIndex > headerRow.FirstCellNum ? startColIndex : headerRow.FirstCellNum);
int cellCount = (colCount > 0 && colCount < headerRow.LastCellNum ? colCount : headerRow.LastCellNum);
List<T> resultList = new List<T>();
for (int i = (headerRowIndex + 1); i <= sheet.LastRowNum; i++)
{
IRow row = sheet.GetRow(i);
if (row != null)
{
List<string> cellValues = new List<string>();
for (int j = cellFirstNum; j < cellCount; j++)
{
if (row.GetCell(j) != null)
{
cellValues.Add(row.GetCell(j).ToNotNullString());
}
else
{
cellValues.Add(string.Empty);
}
}
resultList.Add(buildResultItemFunc(cellValues));
}
}
return resultList;
}
public static MemoryStream ToExcelStream(this IWorkbook book)
{
if (book.NumberOfSheets <= 0)
{
throw new Exception("無有效的sheet數據");
}
MemoryStream stream = new MemoryStream();
stream.Seek(0, SeekOrigin.Begin);
book.Write(stream);
return stream;
}
public static byte[] ToExcelBytes(this IWorkbook book)
{
using (MemoryStream stream = ToExcelStream(book))
{
return stream.ToArray();
}
}
public static JObject HttpUpload(this IWorkbook book, string uploadUrl, IDictionary<string, object> fieldData = null, string exportFileName = null)
{
using (HttpClient client = new HttpClient())
{
MultipartFormDataContent formData = new MultipartFormDataContent();
ByteArrayContent fileContent = new ByteArrayContent(ToExcelBytes(book));
//StreamContent fileContent = new StreamContent(ToExcelStream(book)); 二者都可以
fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
if (string.IsNullOrWhiteSpace(exportFileName))
{
exportFileName = Guid.NewGuid().ToString("N") + ((book is XSSFWorkbook) ? ".xlsx" : ".xls");
}
fileContent.Headers.ContentDisposition.FileName = exportFileName;
fileContent.Headers.ContentDisposition.Name = "file";
formData.Add(fileContent);
Func<string, StringContent> getStringContent = (str) => new StringContent(str, Encoding.UTF8);
if (fieldData != null)
{
foreach (var header in fieldData)
{
formData.Add(getStringContent(header.Value.ToNotNullString()), header.Key);
}
}
HttpResponseMessage res = client.PostAsync(uploadUrl, formData).Result;
string resContent = res.Content.ReadAsStringAsync().Result;
return JObject.Parse(resContent);
}
}
public static string SaveToFile(this IWorkbook book, string filePath)
{
if (File.Exists(filePath))
{
File.SetAttributes(filePath, FileAttributes.Normal);
File.Delete(filePath);
}
using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
book.Write(fs);
}
return filePath;
}
}
}
如上代碼都比較簡單沒有什么復雜要,故不再細述。還有一個Core類,這個只是一個入口幫助類,如果已有獲得IWorkbook的自定義方法,該類可以不用,代碼如下:
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Text;
namespace ExcelEasyUtil
{
/// <summary>
/// NPOI 相關核心入口方法幫助類
/// author:zuowenjun
/// 2019-5-21
/// </summary>
public static class Core
{
/// <summary>
/// 創建一個基本XLSX格式的EXCEL工作薄對象
/// </summary>
/// <returns></returns>
public static IWorkbook CreateXlsxWorkBook()
{
return new XSSFWorkbook();
}
/// <summary>
/// 創建一個基本XLS格式的EXCEL工作薄對象
/// </summary>
/// <returns></returns>
public static IWorkbook CreateXlsWorkBook()
{
return new HSSFWorkbook();
}
/// <summary>
/// 打開指定文件的EXCEL工作薄對象
/// </summary>
/// <param name="filePath">Excel文件路徑</param>
/// <returns></returns>
public static IWorkbook OpenWorkbook(string filePath)
{
bool isCompatible = filePath.EndsWith(".xls", StringComparison.OrdinalIgnoreCase);
var fileStream = System.IO.File.OpenRead(filePath);
if (isCompatible)
{
return new HSSFWorkbook(fileStream);
}
else
{
return new XSSFWorkbook(fileStream);
}
}
}
}
雖然代碼簡單,但方法命名稍有些講究,易閱讀與理解,CreateXlsxWorkBook就是表現創建一個新的基於xlsx格式的工作薄對象,而CreateXlsWorkBook就是表現創建一個新的基於xls格式的工作薄對象,我並沒合成一個方法,然后使用一個bool參數或enum參數來區分,我覺得在這個追求極簡、高效的開發理念中,方法名要易於理解與快速上手,不應產生歧義,也無需太多的參數。OpenWorkbook就是打開一個已有的EXCEL文件工作薄對象。
代碼雖簡單,但我通過合理的封裝入參,委托參數等,實現了簡單但又不失靈活的EXCEL操作方式,沒有一味追求極簡,把所有的復雜操作全部封裝,從而導致本來簡單的事情搞得復雜化,比如:將一個List<T>導出到EXCEL中,常見的實現方式肯定就是通過反射獲得屬性信息,再利用屬性信息獲得值,這是做雖入參簡單,但性能不好,而本文的這個類庫將取值或賦值的方式通過Func委托交由給具體使用的地方,從而避免了反射帶來的性能損失,好了,如下展示幾個使用示例,幫助大家理解和使用。
第一種填充sheet方式:(重點在表格頭的映射設置,通過Lamba屬性表達式與要生成的EXCEL表頭進行關聯映射)
var book= ExcelEasyUtil.Core.CreateXlsxWorkBook()
.FillSheet("人員列表1", peoples,//填充第一個表格
//new Dictionary<string, Expression<Func<People, dynamic>>> //設置表格頭,原始類型
new PropertyColumnMapping<People> //設置表格頭,專用簡化類型
{
{"姓名",p=>p.Name },{"年齡",p=>p.Age },{"生日",p=>p.Birthday },{"住址",p=>p.Address },{"學歷",p=>p.Education },
{ "有工作否",p=>p.hasWork },{"備注",p=>p.Remark }
},
new Dictionary<string, string> //為指定的列設置單元格格式
{
{ "年齡","0歲"},{"生日","yyyy年mm月dd日"}
});
第二種填充sheet方式:(重點在表格頭類型設置【:XXX表示生成的EXCEL該列為某種格式的單元格,如:生日:DT表示是生日這列導出是日期類型格式】,第二個參數返回List<object>這個是可以很好的控制導出時需要的填充數據,可以自由控制數據,比如示例代碼中額外增加了一列判斷的數據列內容,第三個參數是為指定的列設置單元格的具體應用格式)
var book= ExcelEasyUtil.Core.CreateXlsxWorkBook()
.FillSheet("人員列表2", peoples, //填充第二個表格
new List<string>
{
"姓名","年齡:NUM","生日:DT","住址","學歷","有工作否:BL","備注","額外填充列"
}, (p) =>
{
return new List<object> {
p.Name,p.Age,p.Birthday,p.Address,p.Education,p.hasWork?"有":"無",p.Remark,(p.Age<=30 && p.hasWork)?"年輕有為":"要么老了要么沒工作,生活堪憂"
};
}, new Dictionary<string, string>
{
{ "生日","yyyy-mm-dd"}
});
第三種填充sheet方式:(重點仍然是在表頭映射,由於這里的數據源是DataTable,故只是設置DataTable的列與EXCEL要導出的列名映射即可,無需指定列類型,第二個參數是為指定的列設置單元格的具體應用格式)
var book= ExcelEasyUtil.Core.CreateXlsxWorkBook()
.FillSheet("人員列表3", peoplesTable, //填充第三個表格
new Dictionary<string, string> {
{"Name","姓名" },{"Birthday","生日" },{"Address","住址" },{"Education","學歷" }, {"hasWork","有工作否" },{"Remark","備注" }
}
, new Dictionary<string, string>
{
{ "生日","yyyy-mm-dd"}
});
由於實現了FillSheet方法后仍返回IWorkBook實例本身,即可采用鏈式的方式來快速完成多個sheet導出的,合並代碼如下:
string savedPath = ExcelEasyUtil.Core.CreateXlsxWorkBook()
.FillSheet("人員列表1", peoples,//填充第一個表格
//new Dictionary<string, Expression<Func<People, dynamic>>> //設置表格頭,原始類型
new PropertyColumnMapping<People> //設置表格頭,專用簡化類型
{
{"姓名",p=>p.Name },{"年齡",p=>p.Age },{"生日",p=>p.Birthday },{"住址",p=>p.Address },{"學歷",p=>p.Education },
{ "有工作否",p=>p.hasWork },{"備注",p=>p.Remark }
},
new Dictionary<string, string> //為指定的列設置單元格格式
{
{ "年齡","0歲"},{"生日","yyyy年mm月dd日"}
})
.FillSheet("人員列表2", peoples, //填充第二個表格
new List<string>
{
"姓名","年齡:NUM","生日:DT","住址","學歷","有工作否:BL","備注","額外填充列"
}, (p) =>
{
return new List<object> {
p.Name,p.Age,p.Birthday,p.Address,p.Education,p.hasWork?"有":"無",p.Remark,(p.Age<=30 && p.hasWork)?"年輕有為":"要么老了要么沒工作,生活堪憂"
};
}, new Dictionary<string, string>
{
{ "生日","yyyy-mm-dd"}
})
.FillSheet("人員列表3", peoplesTable, //填充第三個表格
new Dictionary<string, string> {
{"Name","姓名" },{"Birthday","生日" },{"Address","住址" },{"Education","學歷" }, {"hasWork","有工作否" },{"Remark","備注" }
}
, new Dictionary<string, string>
{
{ "生日","yyyy-mm-dd"}
})
.SaveToFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "testdata123.xlsx"));
Console.WriteLine("導出EXCEL文件路徑:" + savedPath);
感覺如何,EXCEL導出既能設置列頭,列格式,還能控制填充的數據值,認為是否夠方便呢,我個人覺得還是可以滿足日常大部份的使用EXCEL的需求。
說了導出EXCEL方法再來貼出導入EXCEL數據(這里稱為解析EXCEL數據)的示例用法:
var xlsTable = ExcelEasyUtil.Core.OpenWorkbook(savedPath).ResolveDataTable("人員列表1", 0);
foreach (DataRow row in xlsTable.Rows)
{
string rowStr = string.Join("\t", row.ItemArray);
Console.WriteLine(rowStr);
}
var xlsPeoples = ExcelEasyUtil.Core.OpenWorkbook(savedPath).ResolveAs<People>("人員列表2", 0, list =>
{
return new People
{
Name = list[0],
Birthday = ConvertToDate(list[2]),//日期處理相對較麻煩
Address = list[3]
};
}, 0, 4);
Console.WriteLine("-".PadRight(30,'-'));
foreach (var p in xlsPeoples)
{
string rowStr = string.Join("\t", p.Name, p.Age, p.Birthday, p.Address);
Console.WriteLine(rowStr);
}
這里僅實現了解析到DataTable或解析到所需的對象集合,一般也能滿足常見的導入數據的要求。
當然還有:HttpUpload方法,這個主要是把生成的內存EXCEL文件直接上傳至指定的文件服務器的,如果是在ASP.NET,ASP.NET CORE服務端需要下載,那么則可以采用先使用:book.ToExcelBytes(),然后構造文件字節流Result即可,比如:MVC中的File,可以參照我本文一開始列舉的第二中方法中的導出,在此就不再貼示例了。
好了,以上就是本文的全部內容,可能大神們認為這太簡單了,但我認為簡單高效的實用類庫還是需要的,因為ExcelEasyUtil本身就是為簡單、輕量、實用而封裝的,故不會有太多復雜的東西,如果需要復雜的EXCEL操作就使用我上面說的第四種方案:ExcelUtility。
若有不足之處或好的建議,歡迎評論,謝謝!
本文ExcelEasyUtil源代碼地址:https://github.com/zuowj/ExcelEasyUtil (文中的所有示例代碼在github均有)
為了方便開發者使用,還封裝成了NuGet包:
Packge Manager:Install-Package ExcelEasyUtil -Version 1.0.0
.NET CLI:dotnet add package ExcelEasyUtil --version 1.0.0 OR <PackageReference Include="ExcelEasyUtil" Version="1.0.0" />
