匯出實體檔或不要?
開發這樣的功能其實不難,匯出 Excel 你會,寄送夾帶檔信件你也會,兩段串起來就是,所以問題在那?…問題在生成實體檔案!要知道 ASP.NET 是執行在 Server 端,產出 Excel 檔當然是先存放在 Server 端再進行下一步動作 (ex:提供下載 or 轉寄),這份檔案有時生命週期很短,大部分會在目的達成時立即清除以節省 Server 端硬碟空間,然而夾檔寄送電子郵件其實可以不用生成實體檔,好處是這樣就不存在後續刪除的問題。構想
我的構想是這樣:- 先用 NPOI 匯出 Excel 資料流 (stream)
- 接收資料流以初始化 Attachment 物件,附加到 MailMessage
- 用 SmtpClient 類別發送郵件
總的來說,實作的重點在步驟 2,畢竟以往大家較為習慣的方式是以實體檔夾帶附件,而忽略了 Sytem.Net.Mail.Attachment 類別其實是支援傳入資料流來建構新的執行個體,接下來我將實際演練一次供大家參考。
引用 NPOI
關於 NPOI 的應用,之前也曾經發過一篇文章:利用 NPOI Library 合併多個 Excel 檔,當時的版本是 1.2.1,經過這段時間 NPOI 發展到 1.2.3 beta,以本次需求來說新版本可以運作正常,惟與前一版相較之下,兩者寫法有些許不同,我會用最新版來實作,再補充 1.2.1 stable 版的 NPOI 寫法要如何修改。想用最新版的人可自行上 http://npoi.codeplex.com/ 下載:
下載回來的壓縮檔解開會得到 Ionic.Zip.dll、NPOI.dll 兩個組件,這一版開始 Ionic.Zip.dll 取代了以往的 ICSharpCode.SharpZipLib.dll,並且顯而易見地,NPOI 開發團隊似乎有意在這一版將整個 NPOI 專案編譯成單一 dll。
匯出 Excel 資料流
正式進入實作:- 新增一個網站 (或 Web 應用程式專案),針對上面取得的 NPOI.dll 加入參考 (會一併引用 Ionic.Zip.dll)
- 為方便解說,直接開啟 Default.aspx.cs 撰寫程式,建立一個 DataTable 轉匯 Excel 資料流的方法
01
// using NPOI.HSSF.UserModel;
02
private
Stream RenderDataTableToExcel(DataTable srcTable)
03
{
04
HSSFWorkbook workbook =
new
HSSFWorkbook();
05
HSSFSheet sheet = (HSSFSheet)workbook.CreateSheet();
06
HSSFRow headerRow = (HSSFRow)sheet.CreateRow(0);
07
08
// handling header.
09
foreach
(DataColumn column
in
srcTable.Columns)
10
headerRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName);
11
12
// handling value.
13
int
rowIndex = 1;
14
15
foreach
(DataRow row
in
srcTable.Rows)
16
{
17
HSSFRow dataRow = (HSSFRow)sheet.CreateRow(rowIndex);
18
19
foreach
(DataColumn column
in
srcTable.Columns)
20
{
21
dataRow.CreateCell(column.Ordinal).SetCellValue(row[column].ToString());
22
}
23
24
rowIndex++;
25
}
26
27
MemoryStream stream =
new
MemoryStream();
28
workbook.Write(stream);
29
stream.Flush();
30
stream.Position = 0;
31
32
sheet =
null
;
33
headerRow =
null
;
34
workbook =
null
;
35
36
return
stream;
37
}
- or -
使用 NPOI 1.2.1 stable 的朋友請這樣寫:
01
// using NPOI.HSSF.UserModel;
02
private
Stream RenderDataTableToExcel(DataTable srcTable)
03
{
04
HSSFWorkbook workbook =
new
HSSFWorkbook();
05
HSSFSheet sheet = workbook.CreateSheet();
06
HSSFRow headerRow = sheet.CreateRow(0);
07
08
// handling header.
09
foreach
(DataColumn column
in
srcTable.Columns)
10
headerRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName);
11
12
// handling value.
13
int
rowIndex = 1;
14
15
foreach
(DataRow row
in
srcTable.Rows)
16
{
17
HSSFRow dataRow = sheet.CreateRow(rowIndex);
18
19
foreach
(DataColumn column
in
srcTable.Columns)
20
{
21
dataRow.CreateCell(column.Ordinal).SetCellValue(row[column].ToString());
22
}
23
24
rowIndex++;
25
}
26
27
MemoryStream stream =
new
MemoryStream();
28
workbook.Write(stream);
29
stream.Flush();
30
stream.Position = 0;
31
32
sheet =
null
;
33
headerRow =
null
;
34
workbook =
null
;
35
36
return
stream;
37
}
造成差異的原因參考 NPOI 1.2.3 beta release notes 其中一段:
建構 Attachment
Attachment 的建構函式包含底下列表:第一個參數若是 String 代表傳入實體檔路徑來建構附檔,這是大家最熟悉的方式,而傳入 Stream 則是本次所採用的方式,一樣可以初始化附檔,後續的參數不管是 String 或 ContentType 類型,都是用來指定附檔的內容類型 (Content-Type),其代表 MIME 協定所定義的電子郵件區段內容類型標頭,有興趣研究者可以參考文末連結,暫且簡單帶過,底下是實際撰寫的程式碼:
其餘的動作是使用 SmtpClient 類別經由 gmail 來發信,網路上的範例很多就不多說了,完整程式碼執行效果約略如圖:
範例可從這裡下載:SendAttachmentFromXlsStream.zip