2020年5月29日 星期五

[研究][C#][ASP.NET][WebForm] 把 GridView 匯出成 .csv

[研究][C#][ASP.NET][WebForm] 把 GridView 匯出成 .csv

2020-05-29

********************************************************************************
2020-06-03

如果資料筆數很多,注意效能問題,及 RAM 用量問題。

********************************************************************************

本文使用下面工具

Visual Studio Enterprise 2019 v16.6.0

Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="GridViewExportToCSVDemo.Default" %>
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
<form id="form1" runat="server">
        <div>
            <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
                ConnectionString="<%$ ConnectionStrings:TestDBConnectionString %>" 
                SelectCommand="SELECT * FROM [MyTable]"></asp:SqlDataSource>
            <asp:GridView ID="GridView1" runat="server" AllowPaging="True" AllowSorting="True" 
                AutoGenerateColumns="False" DataKeyNames="SN" DataSourceID="SqlDataSource1">
                <Columns>
                    <asp:BoundField DataField="SN" HeaderText="SN" InsertVisible="False" ReadOnly="True" SortExpression="SN" />
                    <asp:BoundField DataField="myID" HeaderText="myID" SortExpression="myID" />
                    <%--<asp:BoundField DataField="myName" HeaderText="myName" SortExpression="myName" />--%>
                    <asp:BoundField DataField="myName" HeaderText="myName" SortExpression="myName" />
                    <asp:BoundField DataField="purchaseDate" HeaderText="purchaseDate" SortExpression="purchaseDate" />
                </Columns>
            </asp:GridView>
            <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" /><br />
            <br />
            <asp:GridView ID="GridView2" runat="server" AllowPaging="True" AllowSorting="True" 
                Visible="False"
                AutoGenerateColumns="False" DataKeyNames="SN" DataSourceID="SqlDataSource1">
                <Columns>
                    <asp:BoundField DataField="SN" HeaderText="SN" InsertVisible="False" ReadOnly="True" SortExpression="SN" />
                    <asp:BoundField DataField="myID" HeaderText="myID" SortExpression="myID" />
                    <%--<asp:BoundField DataField="myName" HeaderText="myName" SortExpression="myName" />--%>
                    <asp:BoundField DataField="myName" HeaderText="myName" SortExpression="myName" />
                    <asp:BoundField DataField="purchaseDate" HeaderText="purchaseDate" SortExpression="purchaseDate" />
                </Columns>
            </asp:GridView>
            <asp:Button ID="Button2" runat="server" Text="Button" OnClick="Button2_Click" /><br />
            <asp:Label ID="Label_MSG1" runat="server"></asp:Label>
        </div>
    </form>
</body>
</html>



Default.aspx.cs
using System;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace GridViewExportToCSVDemo
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            //不想匯出的列,可以隱藏掉
            //GridView1.Columns[0].Visible = false;
            GridViewExportToCSV(GridView1, "匯出檔名");
        }

        protected void Button2_Click(object sender, EventArgs e)
        {
            GridViewExportToCSV(GridView2, "匯出檔名");
        }
        private string GridViewExportToCSV(GridView gridview, string exportSubject)
        {
            string year = DateTime.Now.Year.ToString();
            
            string mainFileName =  year + "年" + exportSubject;

            //DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".xlsx";
            // 使用者若很多,最好連 fff 也用上
            //DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fff") + ".xlsx";

            // 若檔案名稱有中文字或特殊符號,Response.AddHeader() 輸出檔案名稱要編碼
            string outFileName = HttpUtility.UrlEncode(mainFileName, System.Text.Encoding.UTF8) + "-" +
                DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fff") + ".csv";

            // XLWorkbook.SaveAs() 輸出檔案名稱不要用 HttpUtility.UrlEncode 編碼
            // Response.TransmitFile() 輸出檔案名稱不要用 HttpUtility.UrlEncode 編碼
            string dirFileName = @"D:\Production\TempImageFiles\" + mainFileName + "-" +
                DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fff") + ".csv";
            try
            {
                GridView gv = gridview;

                bool oldAllowPaging = gv.AllowPaging;
                bool oldAllowSorting = gv.AllowSorting;
                gv.AllowSorting = false; // 支援排序時,標題不是文字,要改抓 LinkButton 的文字,麻煩,不如暫時取消
                gv.AllowPaging = false; // 分頁狀態下,只能抓到當頁的10筆,必須取消分頁
                gv.DataBind();

                Response.Clear();
                Response.Buffer = true;
                Response.Charset = "";

                #region Export CSV

                // [EXCEL] Excel打開是亂碼?快速找回資料的最好方法!
                // http://blog.e-happy.com.tw/excel-excel%E6%89%93%E9%96%8B%E6%98%AF%E4%BA%82%E7%A2%BC%EF%BC%9F%E5%BF%AB%E9%80%9F%E6%89%BE%E5%9B%9E%E8%B3%87%E6%96%99%E7%9A%84%E6%9C%80%E5%A5%BD%E6%96%B9%E6%B3%95%EF%BC%81/

                // Excel開啟CSV時的中文編碼問題補遺
                //https://blog.darkthread.net/blog/csv-encoding-again/

                //Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                //Response.AddHeader("content-disposition", "attachment;filename=Export.xlsx");
                //Response.AddHeader("content-disposition", "attachment;filename=" + fileName);
                //Response.AddHeader("content-disposition", "attachment;filename=" + outFileName + "\"");
                //Response.AddHeader("content-disposition", "attachment;filename=" + outFileName);

                //Response.ContentType = "application/CSV";
                Response.ContentType = "application/octet-stream";
                Response.ContentEncoding = Encoding.UTF8;
                //Response.ContentType = "application/vnd.ms-csv";
                Response.AddHeader("content-disposition", "attachment;filename=" + outFileName);
                using (MemoryStream MyMemoryStream = new MemoryStream())
                {
                    //StreamWriter csvFileStreamWriter = new StreamWriter(MyMemoryStream);  // 預設 .csv 是 ANSI,Excel 開啟會亂碼
                    StreamWriter csvFileStreamWriter = new StreamWriter(MyMemoryStream, Encoding.UTF8); // 這樣 Excel 開 .csv 不會亂碼
                    string csvContent = "";

                    int visibleIndex = 0;
                    // 標題純文字這樣抓
                    //GridView1.Columns.Count 是「非自動生成列數」。不管是否為 Visible。
                    //GridView1.HeaderRow.Cells.Count 是「自動生成列數」和「非自動生成列數」的總和。不管是否為 Visible。
                    //GridView1.Visible = false; 時,GridView1.HeaderRow.Cells[i].Visible 總是會 false。

                    // 「非自動生成列」會比「自動生成列」先顯示
                    // HeaderRow.Cells[i].Visible 不管是 true 或 false,回傳都 true
                    // gv.Columns[i].Visible 才回真正回傳 true 或 false
                    // 但是 gv.Columns[i] 只能抓「非自動生成列數」

                    for (int i = 0; i < gv.HeaderRow.Cells.Count; i++)
                    {
                        if (i < gv.Columns.Count)
                        {
                            if (gv.Columns[i].Visible == true)
                            {
                                string columnName = gv.Columns[i].HeaderText;
                                columnName = columnName.Replace("\r", "").Replace("\n", "").Replace(",", ",");
                                if (csvContent == "")
                                {
                                    csvContent = columnName;
                                }
                                else
                                {
                                    csvContent = csvContent + "," + columnName;
                                }
                                visibleIndex++;
                            }
                        }
                        else
                        {
                            // GridView1.Visible = true  時,gv.HeaderRow.Cells[i].Visible 也一定 true
                            // GridView1.Visible = false 時,gv.HeaderRow.Cells[i].Visible 也一定 false
                            // 若想 GridView1.Visible 為 false 時也一定匯出,下面 if 要註解掉
                            //if (gv.HeaderRow.Cells[i].Visible == true)
                            //{
                            string columnName = gv.HeaderRow.Cells[i].Text;

                            columnName = columnName.Replace("\r", "").Replace("\n", "").Replace(",", ",");
                            if (csvContent == "")
                            {
                                csvContent = columnName;
                            }
                            else
                            {
                                csvContent = csvContent + "," + columnName;
                            }
                            visibleIndex++;
                            //}
                        }
                    }
                    csvContent = csvContent + "\r\n";

                    // 資料列這樣抓
                    string lineString = "";
                    for (int i = 0; i < gv.Rows.Count; i++)
                    {
                        visibleIndex = 0;

                        lineString = "";
                        for (int j = 0; j < gv.HeaderRow.Cells.Count; j++)
                        {
                            if (j < gv.Columns.Count)
                            {
                                if (gv.Columns[j].Visible == true)
                                {
                                    // 未執行【將這個欄位轉為 TemplateField】的欄位這樣就可抓到
                                    string dataText = gv.Rows[i].Cells[j].Text;
                                    dataText = dataText.Replace("&nbsp;", "");

                                    // 執行【將這個欄位轉為 TemplateField】的欄位要根據情況改成下面
                                    if (gv.Rows[i].Cells[j].HasControls())
                                    {
                                        for (int k = 0; k < gv.Rows[i].Cells[j].Controls.Count; k++)
                                        {
                                            if (gv.Rows[i].Cells[j].Controls[k] is Label)
                                            {
                                                Label labelControl = gv.Rows[i].Cells[j].Controls[k] as Label;
                                                //Label labelControl = (Label)gv.Rows[i].Cells[j].FindControl("Label1");
                                                if (labelControl != null)
                                                {
                                                    string labelText = labelControl.Text;
                                                    dataText = labelText;
                                                }
                                            }
                                        }
                                    }

                                    dataText = dataText.Replace("\r", "").Replace("\n", "").Replace(",", ",");
                                    if (lineString == "")
                                    {
                                        csvContent = csvContent + dataText;
                                        lineString = "has";
                                    }
                                    else
                                    {
                                        csvContent = csvContent + "," + dataText;
                                    }
                                    visibleIndex++;
                                }
                            }
                            else
                            {
                                // GridView1.Visible = true  時,gv.HeaderRow.Cells[i].Visible 也一定 true
                                // GridView1.Visible = false 時,gv.HeaderRow.Cells[i].Visible 也一定 false
                                // 若想 GridView1.Visible 為 false 時也一定匯出,下面 if 要註解掉
                                //if (gv.HeaderRow.Cells[j].Visible == true)
                                //{
                                // 未執行【將這個欄位轉為 TemplateField】的欄位這樣就可抓到
                                string dataText = gv.Rows[i].Cells[j].Text;

                                // 執行【將這個欄位轉為 TemplateField】的欄位要根據情況改成下面
                                if (gv.Rows[i].Cells[j].HasControls())
                                {
                                    for (int k = 0; k < gv.Rows[i].Cells[j].Controls.Count; k++)
                                    {
                                        if (gv.Rows[i].Cells[j].Controls[k] is Label)
                                        {
                                            Label labelControl = gv.Rows[i].Cells[j].Controls[k] as Label;
                                            //Label labelControl = (Label)gv.Rows[i].Cells[j].FindControl("Label1");
                                            if (labelControl != null)
                                            {
                                                string labelText = labelControl.Text;
                                                dataText = labelText;
                                            }
                                        }
                                    }
                                }

                                dataText = dataText.Replace("\r", "").Replace("\n", "").Replace(",", ",");
                                if (lineString == "")
                                {
                                    csvContent = csvContent + dataText;
                                    lineString = "has";
                                }
                                else
                                {
                                    csvContent = csvContent + "," + dataText;
                                }

                                visibleIndex++;
                                //} // if
                            } // else
                        } // for (int j = 0; j < gv.HeaderRow.Cells.Count; j++)
                        csvContent = csvContent + "\r\n";
                    } // for (int i = 0; i < gv.Rows.Count; i++)

                    csvFileStreamWriter.Write(csvContent);
                    csvFileStreamWriter.Flush();
                    MyMemoryStream.Position = 0;
                    MyMemoryStream.WriteTo(Response.OutputStream);
                    Response.Flush();
                    Response.End();
                }
                #endregion Export CSV

                gv.AllowPaging = oldAllowPaging;
                gv.AllowSorting = oldAllowSorting;
                gv.DataBind();

            }
            catch (Exception ex)
            {
                Label_MSG1.ForeColor = System.Drawing.Color.Green;
                string exMsg = "";
                if (ex != null)
                {
                    exMsg = ex.ToString();
                }
                else
                {
                    exMsg = "不明錯誤。";
                }
                Label_MSG1.Text = exMsg;
                Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript' defer>alert('" + exMsg + "');</script>");
                return exMsg;
            }
            Page.ClientScript.RegisterStartupScript(Page.GetType(), "message", "<script language='javascript' defer>alert('匯出成功。');</script>");
            return "";
        }
    }
}



匯出後,用 Microsoft Excel 2016 開啟正常,沒有警告或錯誤。

(完)

相關文章

[研究][C#][ASP.NET][WebForm] 把 GridView 匯出成 .csv
http://shaurong.blogspot.com/2020/05/caspnetwebform-gridview-csv.html

[研究][C#][ASP.NET][WebForm] 使用 Microsoft OpenXML SDK 2.10.1 ( DocumentFormat.OpenXml ) 把 Excel .xlsx 匯入DocumentFormat.OpenXml, OpenXML
https://shaurong.blogspot.com/2020/04/caspnetwebform-microsoft-openxml-sdk.html

[研究][C#][ASP.NET][WebForm] 使用 OpenXML 2.10.1 (DocumentFormat.OpenXml) 把 GridView 匯出成 .xlsxDocumentFormat.OpenXml, OpenXML
https://shaurong.blogspot.com/2020/04/caspnetwebform-openxml-2101.html

沒有留言:

張貼留言