項目開發過程中,我們要把數據以各種各樣的形式展現給客戶。把數據以文檔的形式展現給客戶相信是一種比較頭疼的問題,如果沒有好的方法會
使得我的開發繁瑣,而且滿足不了客戶的需求。接下來我會通過兩種開發方式介紹如何將數據輸出到Word 文檔上。我會分兩篇文章介紹,第一篇
介紹不使用插件的情況下操作word,第二篇文章將介紹一種強大的插件操作word。下面開始第一篇文章。[本次實例源代碼從這里下載]
文章梗概:
♦ 通過把word另存為html 文件的形式做成模板輸出到word
♦ 通過把word另存為mht 文件的形式做成模板輸出到word
前端定義兩個服務器按鈕:
1 <div id="container" style=""> 2 不利用模板:<br /> 3 <br /> 4 <asp:Button ID="CreateWordBehind" runat="server" Text="輸出數據到word(后臺設置內容)" OnClick="CreateWordBehind_Click" /> 5 <asp:Button ID="CreateWordFront" runat="server" Text="輸出數據到word(前臺設置內容)" OnClick="CreateWordFront_Click" /> 6 <hr /> 7 </div>
后端代碼輸出到word,代碼很簡單只要設置輸出頭為word,而輸出的內容放到 StringBuilder 里,并且通過 StringBuilder實例,去設置要輸出的內容,包括字體、顏色....
1 /// <summary> 2 /// 輸出數據到word(后臺設置內容) 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 protected void CreateWordBehind_Click(object sender, EventArgs e) 7 { 8 HttpContext.Current.Response.Clear(); 9 HttpContext.Current.Response.Buffer = true; 10 HttpContext.Current.Response.Charset = ""; 11 12 // 設置輸出頭 13 HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + System.Web.HttpUtility.UrlEncode("CreateWord", System.Text.Encoding.UTF8) + ".doc"); 14 15 // 設置輸出的編碼格式 16 HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.Default; 17 18 // Response.ContentType指定文件類型 可以為application/ms-excel || application/ms-word || application/ms-txt || application/ms-html 19 // 因為是輸出word 所以這里就指定為 application/ms-word 20 HttpContext.Current.Response.ContentType = "application/ms-word"; 21 22 // 定義StringBuilder 把要輸出的內容寫到里面,并且可以設置字體、顏色、大小等... 23 StringBuilder sb = new StringBuilder(); 24 sb.AppendLine("<div style='text-align:center;font-size:18px;font-weight:bold;'>導出數據到word</div>"); 25 sb.AppendLine("這是導出到word的數據<br />"); 26 sb.AppendLine("<span style='color:blue;'>這是導出到word的數據</span>"); 27 28 HttpContext.Current.Response.Output.Write(sb); 29 HttpContext.Current.Response.Flush(); 30 HttpContext.Current.Response.End(); 31 }
運行、點擊按鈕、打開word文檔,效果如下:
前端設置輸出的html內容。以輸出一個表格為例。其中有兩個div 標記了runat="server",這樣后臺就可以取到這個id,用途一會介紹。
1 <div id="WordContent" runat="server"> 2 <div runat="server" id="title">表頭</div> 3 <table border="1" style="width:400px;line-height: 25px;"> 4 <tr style="width:400px; height:25px;"> 5 <td> 6 編號 7 </td> 8 <td> 9 姓名 10 </td> 11 <td> 12 成績 13 </td> 14 </tr> 15 <tr style="width:400px; height:25px;"> 16 <td> 17 </td> 18 <td> 19 </td> 20 <td> 21 </td> 22 </tr> 23 </table> 24 </div>
至此前端的全部代碼如下:
1 <form id="form1" runat="server"> 2 <div id="WordContent" runat="server"> 3 <div runat="server" id="title">表頭</div> 4 <table border="1" style="width:400px;line-height: 25px;"> 5 <tr style="width:400px; height:25px;"> 6 <td> 7 編號 8 </td> 9 <td> 10 姓名 11 </td> 12 <td> 13 成績 14 </td> 15 </tr> 16 <tr style="width:400px; height:25px;"> 17 <td> 18 </td> 19 <td> 20 </td> 21 <td> 22 </td> 23 </tr> 24 </table> 25 </div> 26 <div id="container" style=""> 27 不利用模板:<br /> 28 <br /> 29 <asp:Button ID="CreateWordBehind" runat="server" Text="輸出數據到word(后臺設置內容)" OnClick="CreateWordBehind_Click" /> 30 <asp:Button ID="CreateWordFront" runat="server" Text="輸出數據到word(前臺設置內容)" OnClick="CreateWordFront_Click" /> 31 <hr /> 32 </div> 33 </form>
后端代碼輸出到word的代碼和就是把之前的StringBuilder 換成了StringWriter,StringWriter的信息其實也是通過Stringbulider存儲的。另外加上一個
HtmlTextWriter對象,HtmlTextWriter可以把前端渲染后的頁面以流的形式輸出。
這是候我們就可以看出在前端html中標記的id的用途了,這樣就可以把id標記為WordContent的里面的內容全部輸出到word,而其他的內容也不會輸出到word上了。
又由于前端html中id標記了title的div標簽在后臺設置了title.Style.Add(HtmlTextWriterStyle.Display, "none");所以它也不會被輸出到word上。
1 /// <summary> 2 /// 輸出數據到word(前臺設置內容) 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 protected void CreateWordFront_Click(object sender, EventArgs e) 7 { 8 HttpContext.Current.Response.Clear(); 9 HttpContext.Current.Response.Buffer = true; 10 HttpContext.Current.Response.Charset = ""; 11 12 // 設置輸出頭 13 HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + System.Web.HttpUtility.UrlEncode("CreateWord", System.Text.Encoding.UTF8) + ".doc"); 14 15 // 設置輸出的編碼格式 16 HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.Default; 17 18 // Response.ContentType指定文件類型 可以為application/ms-excel || application/ms-word || application/ms-txt || application/ms-html 19 // 因為是輸出word 所以這里就指定為 application/ms-word 20 HttpContext.Current.Response.ContentType = "application/ms-word"; 21 22 // 定義輸出流,其信息也是存儲在StringBuilder中的 23 System.IO.StringWriter oStringWriter = new System.IO.StringWriter(); 24 25 // 定義一個服務器控件輸出流(可以把前端渲染后的頁面以流的形式輸出) 26 System.Web.UI.HtmlTextWriter oHtmlTextWriter = new System.Web.UI.HtmlTextWriter(oStringWriter); 27 28 // 控制前端的樣式 (HtmlTextWriterStyle 枚舉用來控制 顯示、字體、大小、顏色...) 29 title.Style.Add(HtmlTextWriterStyle.Display, "none"); 30 31 // 這樣會把前端的整個頁面輸出,由于有些其他元素不需要輸出,所以注釋 32 //this.RenderControl(oHtmlTextWriter); 33 34 // 指定我們前端標記要輸出的容器,容器包含的內容都可以輸出 35 this.WordContent.RenderControl(oHtmlTextWriter); 36 37 HttpContext.Current.Response.Output.Write(oStringWriter); 38 HttpContext.Current.Response.Flush(); 39 HttpContext.Current.Response.End(); 40 }
運行、點擊按鈕發現報錯,提示"只能在執行 Render() 的過程中調用 RegisterForEventValidation;"這個錯誤的原因就是懷疑通過post方法發送惡意的數據,解決方法:
在當前aspx頁面頭部加上:EnableEventValidation="false",默認為true。
1 <%@ Page Language="C#" AutoEventWireup="true" EnableEventValidation="false" CodeBehind="OperateWord.aspx.cs" 2 Inherits="OperateWordPro.OperateWordDemo1.OperateWord" %>
再運行、點擊按鈕、打開word,效果如下(當然需求不可能那么簡單,具體輸出的樣式就要在html中慢慢的調整了):
因為如果用前面的方式,將數據都是自己拼接的,包括樣式全是自己控制,難度將非常大,并且效果也很讓到客戶的滿意(如:要求頁眉、頁腳)。所以如果能夠程序直接讀取word模板把要輸出的內容填充進去將會大大的縮短我們的時間,最主要的是版式不用我們太關心。
1、 通過把word另存為html 文件的形式做成模板輸出到word
把word文檔做成模板的思路就是把word文檔保存為html程序可以直接讀取的形式。由于篇幅的問題,具體把word另存為html的細節可以請參考這篇博文。下面有這樣一個結構的word文檔需要輸出,通過用讀取模板的形式我們只要把下面幾個站位符替換掉就可以了(如,要輸出名字的地方加上了{name}...),思路很簡單。
前臺html代碼:
1 利用模板輸出到word:<br /><br /> 2 <asp:Button ID="CreateWordByHtmlTemplate" runat="server" Text="通過htm模板生成Word" OnClick="CreateWordByHtmlTemplate_Click" /> 3 <asp:Button ID="CreateWordBymhtTemplate" runat="server" Text="通過mht模板生成Word" OnClick="CreateWordBymhtTemplate_Click" />
在項目中添加一個Document文件夾,將模板文件“通知.html”拷貝到這個問價夾下。
下面是后臺代碼:
1 /// <summary> 2 /// 通過htm模板生成Word 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 protected void CreateWordByHtmlTemplate_Click(object sender, EventArgs e) 7 { 8 // 獲取模板的路徑 通過ExprotToWord處理返回 字符串 9 string strWord = DealTemplate(Server.MapPath("../Document/通知.html")); 10 11 Response.ContentEncoding = System.Text.Encoding.Default; 12 Response.ClearContent(); 13 Response.ClearHeaders(); 14 Response.AddHeader("content-disposition", "attachment;filename=合同.doc"); //必須的 15 Response.AddHeader("Content-type", "application"); 16 Response.ContentType = "application/ms-html"; 17 Response.ContentEncoding = System.Text.Encoding.Default; 18 19 Response.Write(strWord); 20 Response.Flush(); 21 Response.Close(); 22 } 23 24 /// <summary> 25 /// 處理模板 返回處理結果 26 /// </summary> 27 /// <param name="templatePath"></param> 28 /// <returns></returns> 29 public string DealTemplate(string templatePath) 30 { 31 StringBuilder sb = new StringBuilder(1024); 32 33 // 讀取文檔內容并轉換成流的形式 編碼為默認編碼 34 StreamReader sr = new StreamReader(templatePath, Encoding.Default); 35 36 // 將流轉換成字符串加進StringBuilder中 37 sb.Append(Encoding.Default.GetString(Encoding.Default.GetBytes(sr.ReadToEnd()))); 38 39 // 把文檔中我們設置的標志位換成我們想要的內容 40 sb.Replace("{name}", "張三"); 41 sb.Replace("{orderNumber}", "ooxx124512"); 42 sb.Replace("{tel}", "1383838383"); 43 44 return sb.ToString(); 45 }
運行、點擊按鈕、打開word,效果如下
2、 通過把word另存為mht 文件的形式做成模板輸出到word
通過把word存儲為html的形式解決了大量手工拼接數據和word的基本版式問題,但是如果這個word中含有圖片和頁眉頁腳就無法通過這種方式處理了,因為html存儲的是單文件,是和圖片這些“其他元素”分開的。所以如果還是要通過模板的方式解決那么這個模板要含有文字、圖片、頁眉設置、頁腳設置。經過一番測試,發現把word另存為.mht的方式可以解決這個問題。上網查了資料說.mht就是htm和圖片等的復合文件。那么我們繼續吧。
制作一個word在上一個word的基礎上添加了一個含有圖片的word:
把這個word另存為"通知2.mht",并復制到Document文件夾下。
后臺代碼和上面代碼一樣,只是換了個模板而已,但是編碼格式要做微調,紅字部分標注了:
1 /// <summary> 2 /// 通過mht模板生成Word 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 protected void CreateWordBymhtTemplate_Click(object sender, EventArgs e) 7 { 8 // 獲取模板的路徑 通過ExprotToWord處理返回 字符串 9 string strWord = DealTemplate(Server.MapPath("../Document/通知2.mht")); 10 11 Response.ContentEncoding = System.Text.Encoding.Default; 12 Response.ClearContent(); 13 Response.ClearHeaders(); 14 Response.AddHeader("content-disposition", "attachment;filename=合同.doc"); //必須的 15 Response.AddHeader("Content-type", "application"); 16 Response.ContentType = "application/ms-html"; 17 Response.ContentEncoding = System.Text.Encoding.Default; 18 19 Response.Write(strWord); 20 Response.Flush(); 21 Response.Close(); 22 } 23 24 /// <summary> 25 /// 處理模板 返回處理結果 26 /// </summary> 27 /// <param name="templatePath"></param> 28 /// <returns></returns> 29 public string DealTemplate(string templatePath) 30 { 31 StringBuilder sb = new StringBuilder(1024); 32 33 // 讀取文檔內容并轉換成流的形式 此處編碼要設置為UTF8 34 StreamReader sr = new StreamReader(templatePath, Encoding.UTF8); 35 36 // 將流轉換成字符串加進StringBuilder中 37 sb.Append(Encoding.Default.GetString(Encoding.Default.GetBytes(sr.ReadToEnd()))); 38 39 // 把文檔中我們設置的標志位換成我們想要的內容 40 sb.Replace("{name}", "張三"); 41 sb.Replace("{orderNumber}", "ooxx124512"); 42 sb.Replace("{tel}", "1383838383"); 43 44 return sb.ToString(); 45 }
運行、點擊按鈕、打開word效果如下:
總結
以上文章介紹的是沒有通過組件將數據輸出到word上,如果客戶需求簡單,格式不是太難控制完全可以根據文章中通過模板的方式輸出到word。其中最容易出問題的我認為就是編碼問題,模板格式不一樣可能程序中的編碼格式也要做相應的調整。
雖然以上方式演示沒問題,但是實際開發中需求并沒有上面的例子簡單。如果客戶要求的word版式比較復雜,且數據要循環輸出那么通過上面任何一種方式都不好解決。但是通過第三方組件就可以解決這種問題,如果這個組件用熟練了,上面的方式我相信你幾乎不會用的。下一篇文章將介紹用第三方組件的方式把內容輸出到word。
文章列表
留言列表