Excel報表開發(本節主要講述導出到Excel操做)

1、Excel導入到GridView以及數據庫操做比較簡單,這兒不作過多講解,須要注意的有二點:html

  一、設置IMEX=1將強制混合數據轉換爲文本。ios

  二、解決Excel驅動程序默認讀取8行:將"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel"目錄下的TypeGuessRows屬性值修改成0。數據庫

2、導出到Excel數組

方法一:安全

用Microsoft.Office.Interop.Excel 組建方式導出,直接遍歷excel每一個單元格插入。
吐槽下此方法慢如蝸牛,不喜歡的跳過。服務器

程序準備app

  一、在項目中添加Microsoft.Office.Interop.Excel的引用,製做一個須要導出的excel樣式模版放到項目某個文件夾下,
  二、用此方法導出須要添加Microsoft.Office.Interop.Excel的引用,且服務器上須要安裝excel,建議服務器上安裝Excel版本低一些,2003就不錯。
  三、服務器權限設定,出現相似檢索 COM 類工廠中 CLSID 爲 {00024500-0000-0000-C000-000000000046}的組件時失敗 的錯誤多半是服務器上的Excel權限沒設好。打開控制面板=>管理工具=>組建服務=>組建服務=>計算機=>個人電腦=>DCOM配置=>Microsoft Excel。右擊屬性標誌選擇交互式用戶 ,安全標籤中的啓動與激活權限選擇自定義而後點擊編輯按鈕,點添加ASP.NET。工具

具體程序以下post

生成的方法字體

複製代碼
public void CreateExcel(System.Data.DataTable dt, string creatName, string MoName)
        {
            // 輸入文件名
            string inputFilePath = System.Web.HttpContext.Current.Server.MapPath(MoName);
            string outFilePath = System.Web.HttpContext.Current.Server.MapPath(creatName);
            //若是文件不存在,則將模板文件拷貝一份做爲輸出文件
            if (!File.Exists(outFilePath))
            {
                File.Copy(inputFilePath, outFilePath, true);
            }
            GC.Collect();
            ApplicationClass myApp = new ApplicationClass();
            Workbook myBook = null;
            Worksheet mySheet = null;
            myApp.Visible = false;
            object oMissiong = System.Reflection.Missing.Value;
            myApp.Workbooks.Open(outFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong
        , oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong); myBook
= myApp.Workbooks[1]; mySheet = (Worksheet)myBook.ActiveSheet; DataTableToExcel(dt, mySheet); myBook.Save(); myBook.Close(true, outFilePath, true); System.Runtime.InteropServices.Marshal.ReleaseComObject(mySheet); System.Runtime.InteropServices.Marshal.ReleaseComObject(myBook); System.Runtime.InteropServices.Marshal.ReleaseComObject(myApp); GC.Collect(); }
複製代碼
複製代碼
public void DataTableToExcel(System.Data.DataTable dt, Worksheet excelSheet)
        {
            int rowCount = dt.Rows.Count;
            int colCount = dt.Columns.Count;
            for (int i = 0; i < rowCount; i++)
            {
                for (int j = 0; j < colCount; j++)
                {
                    mySheet.Cells[i + 1, j] = dt.Rows[i - 1][j - 1].ToString();
                }
            }
        }
複製代碼

調用的方法

CreateExcel ce = new CreateExcel();
            string MoName = "UserFiles/DataIn.xls";//模板的路徑
            string creatName = "UserFiles\\" + "數據導出.xls" ;//生成excel的路徑
            ce.Create(tbl, creatName, MoName);
            Response.Redirect(creatName);

方法二:

用Microsoft.Office.Interop.Excel 組建方式導出,數據依照二維數組方式存放,和第一種方法雖然只有幾行代碼區別,可是速度快了幾十倍,10W條數據15秒鐘左右(小白pc機)具體的服務器配置如方法一
程序以下
生成方法

複製代碼
public void Create(System.Data.DataTable dt, string creatName, string MoName)
        {
            // 輸入文件名
            string inputFilePath = System.Web.HttpContext.Current.Server.MapPath(MoName);
            string outFilePath = System.Web.HttpContext.Current.Server.MapPath(creatName);
            //若是文件不存在,則將模板文件拷貝一份做爲輸出文件
            if (!File.Exists(outFilePath))
            {
                File.Copy(inputFilePath, outFilePath, true);
            }
            GC.Collect();
            ApplicationClass myApp = new ApplicationClass();
            Workbook myBook = null;
            Worksheet mySheet = null;
            myApp.Visible = false;
            object oMissiong = System.Reflection.Missing.Value;
            myApp.Workbooks.Open(outFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong
        , oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong); myBook
= myApp.Workbooks[1]; mySheet = (Worksheet)myBook.ActiveSheet; DataTableToExcel(dt, mySheet); myBook.Save(); myBook.Close(true, outFilePath, true); System.Runtime.InteropServices.Marshal.ReleaseComObject(mySheet); System.Runtime.InteropServices.Marshal.ReleaseComObject(myBook); System.Runtime.InteropServices.Marshal.ReleaseComObject(myApp); GC.Collect(); } public void DataTableToExcel(System.Data.DataTable dt, Worksheet excelSheet) { int rowCount = dt.Rows.Count; int colCount = dt.Columns.Count; object[,] dataArray = new object[rowCount + 1, colCount]; for (int i = 0; i < rowCount; i++) { for (int j = 0; j < colCount; j++) { dataArray[i, j] = dt.Rows[i][j]; } } //從A2開始導入寫入數據便可以避開表頭被重寫 excelSheet.get_Range("A2", excelSheet.Cells[rowCount + 1, colCount]).Value2 = dataArray; }
複製代碼

調用方法

CreateExcel ce = new CreateExcel();
            string MoName = "UserFiles/DataIn.xls";//模板的路徑
            string creatName = "UserFiles\\" + "數據導出.xls" ;//生成excel的路徑
            ce.Create(tbl, creatName, MoName);
            Response.Redirect(creatName);

 

方法三:

此方法不須要在服務器上安裝Excel,採用生成xmlexcel方式輸出到客戶端,可能須要客戶機安裝excel(心虛沒試過)因此也不會有亂七八糟的權限設定,和莫名其妙的版本問題。並且此種方法比第二種更快試了下10W條數據(20列)不到10秒(小白pc)。

方法以下

生成方法:

複製代碼
public static string CreateExcel(DataTable dt, List<string> columnNames)
    {
        StringBuilder strb = new StringBuilder();
        strb.Append(" <html xmlns:o=\"urn:schemas-microsoft-com:office:office\"");
        strb.Append("xmlns:x=\"urn:schemas-microsoft-com:office:excel\"");
        strb.Append("xmlns=\"http://www.w3.org/TR/REC-html40\"");
        strb.Append(" <head> <meta http-equiv='Content-Type' content='text/html; charset=gb2312'>");
        strb.Append(" <style>");
        strb.Append(".xl26");
        strb.Append(" {mso-style-parent:style0;");
        strb.Append(" font-family:\"Times New Roman\", serif;");
        strb.Append(" mso-font-charset:0;");
        strb.Append(" mso-number-format:\"@\";}");
        strb.Append(" </style>");
        strb.Append(" <xml>");
        strb.Append(" <x:ExcelWorkbook>");
        strb.Append(" <x:ExcelWorksheets>");
        strb.Append(" <x:ExcelWorksheet>");
        strb.Append(" <x:Name>Sheet1 </x:Name>");
        strb.Append(" <x:WorksheetOptions>");
        strb.Append(" <x:DefaultRowHeight>285 </x:DefaultRowHeight>");
        strb.Append(" <x:Selected/>");
        strb.Append(" <x:Panes>");
        strb.Append(" <x:Pane>");
        strb.Append(" <x:Number>3 </x:Number>");
        strb.Append(" <x:ActiveCol>1 </x:ActiveCol>");
        strb.Append(" </x:Pane>");
        strb.Append(" </x:Panes>");
        ////設置工做表只讀屬性
        //strb.Append(" <x:ProtectContents>False </x:ProtectContents>");
        //strb.Append(" <x:ProtectObjects>False </x:ProtectObjects>");
        //strb.Append(" <x:ProtectScenarios>False </x:ProtectScenarios>");
        strb.Append(" </x:WorksheetOptions>");
        strb.Append(" </x:ExcelWorksheet>");
        strb.Append(" <x:WindowHeight>6750 </x:WindowHeight>");
        strb.Append(" <x:WindowWidth>10620 </x:WindowWidth>");
        strb.Append(" <x:WindowTopX>480 </x:WindowTopX>");
        strb.Append(" <x:WindowTopY>75 </x:WindowTopY>");
        strb.Append(" <x:ProtectStructure>False </x:ProtectStructure>");
        strb.Append(" <x:ProtectWindows>False </x:ProtectWindows>");
        strb.Append(" </x:ExcelWorkbook>");
        strb.Append(" </xml>");
        strb.Append("");
        strb.Append(" </head> <body> <table align=\"center\" style='border-collapse:collapse;table-layout:fixed'> <tr>");
        //if (ds.Tables.Count > 0)
        //{
        //寫列標題
        int columncount = columnNames.Count;
        for (int columi = 0; columi < columncount; columi++)
        {
            strb.Append(" <td> <b>" + columnNames[columi] + " </b> </td>");
        }
        strb.Append(" </tr>");
        //寫數據
        for (int i = 0; i < dt.Rows.Count; i++)
        {
            strb.Append(" <tr>");
            for (int j = 0; j < dt.Columns.Count; j++)
            {
                strb.Append(" <td class='xl26'>" + dt.Rows[i][j].ToString() + " </td>");
            }
            strb.Append(" </tr>");
        }
        //}
        strb.Append(" </body> </html>");
        return strb.ToString();
        
    }
複製代碼
 

調用方法:

複製代碼
protected void Button1_Click(object sender, EventArgs e)
    {
        string strSql = 「select * from table1";
        System.Data.DataTable tbl = SqlHelper.GetDateTable(strSql);
        List<string> names = new List<string>() {
        "序號","固定資產管理碼","品名","資產大類","資產小類","品牌","規格型號","編碼1","編碼2","使用部門","責任人","使用人","放置地點","價格"

,"購置日期","供應商","保修截至日期","預計使用月份","備註","其餘" }; string str = ExcelHelper.CreateExcel(tbl, names); Response.Clear(); Response.Buffer = true; Response.Charset = "GB2312"; Response.AppendHeader("Content-Disposition", "attachment;filename=1213.xls"); Response.ContentEncoding = System.Text.Encoding.GetEncoding("GB2312");//設置輸出流爲簡體中文 Response.ContentType = "application/ms-excel";//設置輸出文件類型爲excel文件。 //this.EnableViewState = false; Response.Write(str); Response.End(); }
複製代碼
 

3、C#操做Excel常見問題

 

常常會有項目須要把表格導出爲 Excel 文件,或者是導入一份 Excel 來操做,那麼如何在 C# 中操做 Excel 文件成了一個最基本的問題。

作開發這幾年來,陸陸續續也接觸過這樣的需求,但由於不頻繁,因此常常是遇到問題再去網上搜。最近的一個項目,要導出的這個 Excel 涉及了不少比較偏僻的操做,因此決定在這裏開一篇文章,專門用來收集和整理使用到的代碼,以及一些技巧。若是各位看官有一些本身的心得,或者有更好的方案,也歡迎交流。我會時不時更新一下。

 

0. 使用以前

在寫代碼以前,咱們須要先添加引用,在 程序集 – 擴展 裏面:Microsoft.Office.Interop.Excel。 
還有要注意的是,引用以後,須要將屬性中的「嵌入互操做類型」設置爲 Flase,否則編譯時可能會出錯。 
而後就是記得 using 啦:

using Microsoft.Office.Interop.Excel;
using System.Reflection;

▲ 這裏的第二個 using 是由於在 Excel 操做中會常常用到一個 Missing.Value 的默認值,因此須要先引用 System.Reflection。

 

1. 開始使用

通常在使用中,咱們只是操做一份 Excel 中的第一個工做表(sheet),下面來最簡單的建立和讀取一份 Excel 中的第一個 sheet。

複製代碼
// 定義一個 Missing 的值,方便後面使用
Missing miss = Missing.Value;

// 建立 Excel,並制定是不可見的
ApplicationClass excel = new ApplicationClass();
excel.Visible = false;

// 新建一份電子表格,或者打開現有的文件
Workbook wb = excel.Workbooks.Add();
Workbook wb = excel.Workbooks.Open("demo.xls");

// 取獲得第一個工做表,或者取得當前默認的工做表
Worksheet ws = wb.Sheets[1] as Worksheet;
Worksheet ws = wb.ActiveSheet as Worksheet;
複製代碼

▲ 注意,在 Excel 的操做中,許多時候,索引是從 1 開始的,而不是 0,這和大多數程序語法有區別。

 

2. 使用以後

既然已經把 Excel 都建立出來了,咱們就先來講說使用以後的結束,以及保存文件,須要注意的問題。這就比如在寫代碼,兩個花括號都是同時敲的,事後再來寫裏面的代碼。

許多資料中,Excel 使用以後都直接就 excel = null; 來結束代碼,這些朋友難道沒發現,這樣會在系統中留下許多 EXCEL.EXE 的進程嗎?以下圖:

若是這是在用戶的客戶端,可能會由於關機,而把這個問題忽略。但若是是在服務器上生成 Excel 文件,一個用戶生成一次,就產生一個進程,那麼後果可想而知。因此咱們要來先說說使用以後怎麼結束 Excel 的進程。 
結束 Excel 不能單單把 EXCEL.EXE 結束就好,這樣的話,若是用戶正好打開了一個 Excel 也會被結束掉。 
下面是正確結束 Excel 的代碼:

複製代碼
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);

// 結束 Excel 進程
public static void KillExcel(Application excel)
{
    IntPtr t = new IntPtr(excel.Hwnd);
    int k = 0;
    GetWindowThreadProcessId(t, out k);
    System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(k);
    p.Kill();
}
複製代碼

▲ 注意 DllImport 須要 using System.Runtime.InteropServices 。

接下來就是比較保險的關閉,以及調用上面的代碼:

複製代碼
// 關閉電子表格,釋放資源
wb.Close();
wb = null;

// 退出 Excel,釋放資源
excel.Quit();
KillExcel(excel);
excel = null;
複製代碼

 完全關閉excel.exe,還有一種方法關閉以後GC.Collect();強制資源回收,這樣excel.exe就沒有了

3. 保存時的格式問題

「文件格式和擴展名不匹配。文件可能已損壞或不安全。」看起來彷佛很嚴重,尤爲對一些電腦小白來講,不安全這個詞很耀眼。這個問題,可能不少朋友在作 Excel 導出的時候,都會遇到,包括我本身使用一些軟件也遇過,絕大多數都沒有對這個問題進行處理,以爲讓用戶點一下「是」就行了。但對於我這種相對注重用戶體驗的開發者來講,這樣固然是不行的。那麼究竟是什麼形成的? 
其實解決這個問題很是簡單,之因此會出現這個問題,是和 Office Excel 的版本有關係。咱們都知道 Excel 有一個 97-2003 的格式,就是最多見的 .xls 文件,除了這個還有一種 .xlsx 文件,這種是自Office Excel 2007 以後有的新格式,除了這些,Excel 還支持把表格保存爲 .xml 甚至是純文本的格式。而咱們用程序在生成 Excel 的時候,考慮到國內還有一大批 Office 2003 的使用者,因此咱們都會保存爲 .xls 以便更好的兼容他們。大部分都是像下面的代碼這個保存的:

// 保存
wb.SaveAs("demo.xls");

雖然你的保存路徑中有包含 .xls 後綴名,但其實這個時候,Excel 並不知道你是要以什麼格式去保存,因此它多是無格式的,或者是當前系統中 Office 版本的默認格式。 
如今咱們來看看 SaveAs 的參數中,會發現還有第二個參數 FileFormat,顧名思義,就是文件格式,正好是咱們要的參數,因此咱們只要告訴 Excel 要保存的格式,問題迎面而解:

// 保存,格式編碼爲56(xls)(.xlsx 的編碼爲51)
wb.SaveAs("demo.xls", 56);

如今再打開生成的 demo.xls 文件,會發現直接就打開了,不會再出現上面的問題了。

 

4. 經常使用的格式設置

在操做 Excel 的時候,除了上面這些基礎問題,還有就是一些常見的格式設置。包括字體字號、粗體、合併單元格、垂直居中、橫向居中、行高、列寬、單元格格式,邊框樣式,等等。下面的代碼就包含了這些經常使用的設置:

複製代碼
// 選擇一塊區域(一個或多個單元格之間)
Range range = ws.get_Range(ws.Cells[1, 1], ws.Cells[2, 10]);

// 設定單元格格式,@是指文本格式(導出一串長數字時,例如手機號碼,會被處理爲數字,因此咱們要強制爲文本)
range.NumberFormat = "@";

// 合併單元格
range.MergeCells = true;

// 設置行高、列寬
range.RowHeight = 35;
range.ColumnWidth = 100;

// 設置字體字號、粗體、字體(還有大多數字體相關的都在 Font 屬性中)
range.Font.Size = 12;
range.Font.Bold = true;
range.Font.Name = "楷體";

// 橫向居中、垂直居中
range.HorizontalAlignment = XlHAlign.xlHAlignCenter;
range.VerticalAlignment = XlHAlign.xlHAlignCenter;

// 設置邊框
range.Borders.LineStyle = 1;
range.Borders.LineStyle = XlLineStyle.xlContinuous;

// 給單元格設置值(內容)
range.set_Value(miss, "abel.cnblogs.com");
複製代碼

比較麻煩的是,每操做一個區域,咱們就要從新設定一次 Range,這至關於在 Excel 實際作了一次選擇某些單元格的操做,因此數據量大的話,生成 Excel 的速度會有點慢。

 

5. 經常使用的打印設置

這部分可能在網上比較少見,但一些項目中也會有相關的需求,好比要默認橫向紙張啦,打印標題行啦(就是無論打印到第幾頁,都會出現這一行,通常是表格第一行)等等,咱們來看看代碼吧:

// 設置橫向紙張
ws.PageSetup.Orientation = XlPageOrientation.xlLandscape;

// 設置打印標題的範圍
ws.PageSetup.PrintTitleRows = "$3:$3";

 

6. 其它設置

還有一些其它的設置和操做,暫時先都整理在這吧。

// 設置電子表格的名稱
ws.Name = "Hello C#";

 

7. 關於生成速度慢的解決方案…

上面咱們有提到,使用程序來生成 Excel,遇到數據量大的話(十萬級的數據就足夠了),會比較慢的問題,這個怎麼破? 
其實咱們能夠不使用 Excel 操做類來生成,而是直接用 IO 來生成 xml 格式的 Excel 表格,最後保存爲 .xls 文件便可,速度能夠提升N倍。固然,會出現上面第三點說的問題。 
那有沒有速度又快,又不會出現那個安全提示的方法呢?也是有的,來看看代碼:

// 打開 XML 格式的 Excel 文件
Workbook wb = excel.Workbooks.OpenXML("temp.xml");

// 再保存爲真正 xls 格式的 Excel 文件
wb.SaveAs("demo.xls", 56);

是的,方法就是先打開一份用 IO 生成好的 xml 格式的 Excel,再另存爲 Office 97-2003 格式的 xls 文件。 
注意,這個方法只適合格式簡單的 Excel,否則在保存的時候,會提示兼容問題!

相關文章
相關標籤/搜索