本篇主要介紹C#的Excel導入、導出。html
目錄
1. 介紹:描述第三方類庫NPOI以及Excel結構git
2. Excel導入:介紹C#如何調用NPOI進行Excel導入,包含:流程圖、NOPI以及C#代碼github
3. Excel導出:介紹C#如何調用NPOI進行Excel導出,包含:流程圖、NOPI以、C#代碼以及代碼分析web
4. 源碼下載:展現運行圖及源碼下載 數據庫
1. 介紹
1.1 第三方類庫:NPOI
說明:NPOI是POI項目的.NET 版本,可用於Excel、Word的讀寫操做。服務器
優勢:不用裝Office環境。app
下載地址:http://npoi.codeplex.com/releasespost
1.2 Excel結構介紹
工做簿(Workbook):每一個Excel文件可理解爲一個工做簿。ui
工做表(Sheet):一個工做簿(Workbook)能夠包含多個工做表。url
行(row):一個工做表(Sheet)能夠包含多個行。
2. Excel導入
2.1 操做流程
2.2 NPOI操做代碼
說明:把Excel文件轉換爲List<T>
步驟:
①讀取Excel文件並以此初始化一個工做簿(Workbook);
②從工做簿上獲取一個工做表(Sheet);默認爲工做薄的第一個工做表;
③遍歷工做表全部的行(row);默認從第二行開始遍歷,第一行(序號0)爲單元格頭部;
④遍歷行的每個單元格(cell),根據必定的規律賦值給對象的屬性。
代碼:
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
|
/// <summary>
/// 從Excel2003取數據並記錄到List集合裏
/// </summary>
/// <param name="cellHeard">單元頭的Key和Value:{ { "UserName", "姓名" }, { "Age", "年齡" } };</param>
/// <param name="filePath">保存文件絕對路徑</param>
/// <param name="errorMsg">錯誤信息</param>
/// <returns>轉換好的List對象集合</returns>
private
static
List<T> Excel2003ToEntityList<T>(Dictionary<
string
,
string
> cellHeard,
string
filePath,
out
StringBuilder errorMsg)
where
T :
new
()
{
errorMsg =
new
StringBuilder();
// 錯誤信息,Excel轉換到實體對象時,會有格式的錯誤信息
List<T> enlist =
new
List<T>();
// 轉換後的集合
List<
string
> keys = cellHeard.Keys.ToList();
// 要賦值的實體對象屬性名稱
try
{
using
(FileStream fs = File.OpenRead(filePath))
{
HSSFWorkbook workbook =
new
HSSFWorkbook(fs);
HSSFSheet sheet = (HSSFSheet)workbook.GetSheetAt(0);
// 獲取此文件第一個Sheet頁
for
(
int
i = 1; i <= sheet.LastRowNum; i++)
// 從1開始,第0行爲單元頭
{
// 1.判斷當前行是否空行,若空行就不在進行讀取下一行操做,結束Excel讀取操做
if
(sheet.GetRow(i) ==
null
)
{
break
;
}
T en =
new
T();
string
errStr =
""
;
// 當前行轉換時,是否有錯誤信息,格式爲:第1行數據轉換異常:XXX列;
for
(
int
j = 0; j < keys.Count; j++)
{
// 2.若屬性頭的名稱包含'.',就表示是子類裏的屬性,那麼就要遍歷子類,eg:UserEn.TrueName
if
(keys[j].IndexOf(
"."
) >= 0)
{
// 2.1解析子類屬性
string
[] properotyArray = keys[j].Split(
new
string
[] {
"."
}, StringSplitOptions.RemoveEmptyEntries);
string
subClassName = properotyArray[0];
// '.'前面的爲子類的名稱
string
subClassProperotyName = properotyArray[1];
// '.'後面的爲子類的屬性名稱
System.Reflection.PropertyInfo subClassInfo = en.GetType().GetProperty(subClassName);
// 獲取子類的類型
if
(subClassInfo !=
null
)
{
// 2.1.1 獲取子類的實例
var
subClassEn = en.GetType().GetProperty(subClassName).GetValue(en,
null
);
// 2.1.2 根據屬性名稱獲取子類裏的屬性信息
System.Reflection.PropertyInfo properotyInfo = subClassInfo.PropertyType.GetProperty(subClassProperotyName);
if
(properotyInfo !=
null
)
{
try
{
// Excel單元格的值轉換爲對象屬性的值,若類型不對,記錄出錯信息
properotyInfo.SetValue(subClassEn, GetExcelCellToProperty(properotyInfo.PropertyType, sheet.GetRow(i).GetCell(j)),
null
);
}
catch
(Exception e)
{
if
(errStr.Length == 0)
{
errStr =
"第"
+ i +
"行數據轉換異常:"
;
}
errStr += cellHeard[keys[j]] +
"列;"
;
}
}
}
}
else
{
// 3.給指定的屬性賦值
System.Reflection.PropertyInfo properotyInfo = en.GetType().GetProperty(keys[j]);
if
(properotyInfo !=
null
)
{
try
{
// Excel單元格的值轉換爲對象屬性的值,若類型不對,記錄出錯信息
properotyInfo.SetValue(en, GetExcelCellToProperty(properotyInfo.PropertyType, sheet.GetRow(i).GetCell(j)),
null
);
}
catch
(Exception e)
{
if
(errStr.Length == 0)
{
errStr =
"第"
+ i +
"行數據轉換異常:"
;
}
errStr += cellHeard[keys[j]] +
"列;"
;
}
}
}
}
// 如有錯誤信息,就添加到錯誤信息裏
if
(errStr.Length > 0)
{
errorMsg.AppendLine(errStr);
}
enlist.Add(en);
}
}
return
enlist;
}
catch
(Exception ex)
{
throw
ex;
}
}
|
2.3 C#邏輯操做代碼
說明:對Excel轉換後的List<T>進行後續操做;如:檢測有效性、持久化存儲等等
步驟:
①調用2.2代碼,把Excel文件轉換爲List<T>。
②對List<T>進行有效性檢測:必填項是否爲空、是否有重複記錄等等。
③對List<T>進行持久化存儲操做。如:存儲到數據庫。
④返回操做結果。
代碼:
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
|
public
void
ImportExcel(HttpContext context)
{
StringBuilder errorMsg =
new
StringBuilder();
// 錯誤信息
try
{
#region 1.獲取Excel文件並轉換爲一個List集合
// 1.1存放Excel文件到本地服務器
HttpPostedFile filePost = context.Request.Files[
"filed"
];
// 獲取上傳的文件
string
filePath = ExcelHelper.SaveExcelFile(filePost);
// 保存文件並獲取文件路徑
// 單元格擡頭
// key:實體對象屬性名稱,可經過反射獲取值
// value:屬性對應的中文註解
Dictionary<
string
,
string
> cellheader =
new
Dictionary<
string
,
string
> {
{
"Name"
,
"姓名"
},
{
"Age"
,
"年齡"
},
{
"GenderName"
,
"性別"
},
{
"TranscriptsEn.ChineseScores"
,
"語文成績"
},
{
"TranscriptsEn.MathScores"
,
"數學成績"
},
};
// 1.2解析文件,存放到一個List集合裏
List<UserEntity> enlist = ExcelHelper.ExcelToEntityList<UserEntity>(cellheader, filePath,
out
errorMsg);
#endregion
#region 2.對List集合進行有效性校驗
#region 2.1檢測必填項是否必填
for
(
int
i = 0; i < enlist.Count; i++)
{
UserEntity en = enlist[i];
string
errorMsgStr =
"第"
+ (i + 1) +
"行數據檢測異常:"
;
bool
isHaveNoInputValue =
false
;
// 是否含有未輸入項
if
(
string
.IsNullOrEmpty(en.Name))
{
errorMsgStr +=
"姓名列不能爲空;"
;
isHaveNoInputValue =
true
;
}
if
(isHaveNoInputValue)
// 若必填項有值未填
{
en.IsExcelVaildateOK =
false
;
errorMsg.AppendLine(errorMsgStr);
}
}
#endregion
#region 2.2檢測Excel中是否有重複對象
for
(
int
i = 0; i < enlist.Count; i++)
{
UserEntity enA = enlist[i];
if
(enA.IsExcelVaildateOK ==
false
)
// 上面驗證不經過,不進行此步驗證
{
continue
;
}
for
(
int
j = i + 1; j < enlist.Count; j++)
{
UserEntity enB = enlist[j];
// 判斷必填列是否所有重複
if
(enA.Name == enB.Name)
{
enA.IsExcelVaildateOK =
false
;
enB.IsExcelVaildateOK =
false
;
errorMsg.AppendLine(
"第"
+ (i + 1) +
"行與第"
+ (j + 1) +
"行的必填列重複了"
);
}
}
}
#endregion
// TODO:其餘檢測
#endregion
// 3.TODO:對List集合持久化存儲操做。如:存儲到數據庫
// 4.返回操做結果
bool
isSuccess =
false
;
if
(errorMsg.Length == 0)
{
isSuccess =
true
;
// 若錯誤信息成都爲空,表示無錯誤信息
}
var
rs =
new
{ success = isSuccess, msg = errorMsg.ToString(), data = enlist };
System.Web.Script.Serialization.JavaScriptSerializer js =
new
System.Web.Script.Serialization.JavaScriptSerializer();
context.Response.ContentType =
"text/plain"
;
context.Response.Write(js.Serialize(rs));
// 返回Json格式的內容
}
catch
(Exception ex)
{
throw
ex;
}
}
|
3. Excel導出
3.1 導出流程
3.2 NPOI操做代碼
說明:把List<T>轉換爲Excel
步驟:
①建立一個工做簿(Workbook);
②在工做簿上建立一個工做表(Sheet);
③在工做表上建立第一行(row),第一行爲列頭,依次寫入cellHeard的值(作爲列名)。
④循環遍歷List<T>集合,每循環一遍建立一個行(row),而後根據cellHeard的鍵(屬性名稱)依次從List<T>中的實體對象取值存放到單元格內。
代碼:
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
|
/// <summary>
/// 實體類集合導出到Excle2003
/// </summary>
/// <param name="cellHeard">單元頭的Key和Value:{ { "UserName", "姓名" }, { "Age", "年齡" } };</param>
/// <param name="enList">數據源</param>
/// <param name="sheetName">工做表名稱</param>
/// <returns>文件的下載地址</returns>
public
static
string
EntityListToExcel2003(Dictionary<
string
,
string
> cellHeard, IList enList,
string
sheetName)
{
try
{
string
fileName = sheetName +
"-"
+ DateTime.Now.ToString(
"yyyyMMddHHmmssfff"
) +
".xls"
;
// 文件名稱
string
urlPath =
"UpFiles/ExcelFiles/"
+ fileName;
// 文件下載的URL地址,供給前臺下載
string
filePath = HttpContext.Current.Server.MapPath(
"\\"
+ urlPath);
// 文件路徑
// 1.檢測是否存在文件夾,若不存在就創建個文件夾
string
directoryName = Path.GetDirectoryName(filePath);
if
(!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
// 2.解析單元格頭部,設置單元頭的中文名稱
HSSFWorkbook workbook =
new
HSSFWorkbook();
// 工做簿
ISheet sheet = workbook.CreateSheet(sheetName);
// 工做表
IRow row = sheet.CreateRow(0);
List<
string
> keys = cellHeard.Keys.ToList();
for
(
int
i = 0; i < keys.Count; i++)
{
row.CreateCell(i).SetCellValue(cellHeard[keys[i]]);
// 列名爲Key的值
}
// 3.List對象的值賦值到Excel的單元格里
int
rowIndex = 1;
// 從第二行開始賦值(第一行已設置爲單元頭)
foreach
(
var
en
in
enList)
{
IRow rowTmp = sheet.CreateRow(rowIndex);
for
(
int
i = 0; i < keys.Count; i++)
// 根據指定的屬性名稱,獲取對象指定屬性的值
{
string
cellValue =
""
;
// 單元格的值
object
properotyValue =
null
;
// 屬性的值
System.Reflection.PropertyInfo properotyInfo =
null
;
// 屬性的信息
// 3.1 若屬性頭的名稱包含'.',就表示是子類裏的屬性,那麼就要遍歷子類,eg:UserEn.UserName
if
(keys[i].IndexOf(
"."
) >= 0)
{
// 3.1.1 解析子類屬性(這裏只解析1層子類,多層子類未處理)
string
[] properotyArray = keys[i].Split(
new
string
[] {
"."
}, StringSplitOptions.RemoveEmptyEntries);
string
subClassName = properotyArray[0];
// '.'前面的爲子類的名稱
string
subClassProperotyName = properotyArray[1];
// '.'後面的爲子類的屬性名稱
System.Reflection.PropertyInfo subClassInfo = en.GetType().GetProperty(subClassName);
// 獲取子類的類型
if
(subClassInfo !=
null
)
{
// 3.1.2 獲取子類的實例
var
subClassEn = en.GetType().GetProperty(subClassName).GetValue(en,
null
);
// 3.1.3 根據屬性名稱獲取子類裏的屬性類型
properotyInfo = subClassInfo.PropertyType.GetProperty(subClassProperotyName);
if
(properotyInfo !=
null
)
{
properotyValue = properotyInfo.GetValue(subClassEn,
null
);
// 獲取子類屬性的值
}
}
}
else
{
// 3.2 若不是子類的屬性,直接根據屬性名稱獲取對象對應的屬性
properotyInfo = en.GetType().GetProperty(keys[i]);
if
(properotyInfo !=
null
)
{
properotyValue = properotyInfo.GetValue(en,
null
);
}
}
// 3.3 屬性值通過轉換賦值給單元格值
if
(properotyValue !=
null
)
{
cellValue = properotyValue.ToString();
// 3.3.1 對時間初始值賦值爲空
if
(cellValue.Trim() ==
"0001/1/1 0:00:00"
|| cellValue.Trim() ==
"0001/1/1 23:59:59"
)
{
cellValue =
""
;
}
}
// 3.4 填充到Excel的單元格里
rowTmp.CreateCell(i).SetCellValue(cellValue);
}
rowIndex++;
}
// 4.生成文件
FileStream file =
new
FileStream(filePath, FileMode.Create);
workbook.Write(file);
file.Close();
// 5.返回下載路徑
return
urlPath;
}
catch
(Exception ex)
{
throw
ex;
}
}
|
3.3 C#邏輯操做代碼
說明:對Excel轉換後的List<T>進行後續操做;如:檢測有效性、持久化存儲等等
步驟:
①獲取List<T>集合。
②調用3.2,將List<T>轉換爲Excel文件。
③服務器存儲Excel文件並返回下載連接。
代碼:
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
|
public
void
ExportExcel(HttpContext context)
{
try
{
// 1.獲取數據集合
List<UserEntity> enlist =
new
List<UserEntity>() {
new
UserEntity{Name=
"劉一"
,Age=22,Gender=
"Male"
,TranscriptsEn=
new
TranscriptsEntity{ChineseScores=80,MathScores=90}},
new
UserEntity{Name=
"陳二"
,Age=23,Gender=
"Male"
,TranscriptsEn=
new
TranscriptsEntity{ChineseScores=81,MathScores=91} },
new
UserEntity{Name=
"張三"
,Age=24,Gender=
"Male"
,TranscriptsEn=
new
TranscriptsEntity{ChineseScores=82,MathScores=92} },
new
UserEntity{Name=
"李四"
,Age=25,Gender=
"Male"
,TranscriptsEn=
new
TranscriptsEntity{ChineseScores=83,MathScores=93} },
new
UserEntity{Name=
"王五"
,Age=26,Gender=
"Male"
,TranscriptsEn=
new
TranscriptsEntity{ChineseScores=84,MathScores=94} },
};
// 2.設置單元格擡頭
// key:實體對象屬性名稱,可經過反射獲取值
// value:Excel列的名稱
Dictionary<
string
,
string
> cellheader =
new
Dictionary<
string
,
string
> {
{
"Name"
,
"姓名"
},
{
"Age"
,
"年齡"
},
{
"GenderName"
,
"性別"
},
{
"TranscriptsEn.ChineseScores"
,
"語文成績"
},
{
"TranscriptsEn.MathScores"
,
"數學成績"
},
};
// 3.進行Excel轉換操做,並返回轉換的文件下載連接
string
urlPath = ExcelHelper.EntityListToExcel2003(cellheader, enlist,
"學生成績"
);
System.Web.Script.Serialization.JavaScriptSerializer js =
new
System.Web.Script.Serialization.JavaScriptSerializer();
context.Response.ContentType =
"text/plain"
;
context.Response.Write(js.Serialize(urlPath));
// 返回Json格式的內容
}
catch
(Exception ex)
{
throw
ex;
}
}
|
3.4 代碼分析
核心代碼主要是cellheader與List<T>之間的映射關係:
4. 源碼下載
4.1 運行圖
4.2 下載地址
百度網盤: http://pan.baidu.com/s/1o69We8M
CSDN:http://download.csdn.net/download/polk6/8974195
2018/07/11更新,添加對Excel 2007的導入支持:
git:https://github.com/polk6/CSharp-Excel