Search code examples
c#asp.netjqueryajaxpagemethods

Exporting HTML Table to Excel via Page Method


I have the following code on the client side for retrieving data from the HTML Table and then send it to the server through the page method:

function dtExportToCSV(dataTable) {
    var elements = dtDataToJSON(dataTable);
    var headers = dtHeadersToJSON(tableSelector);

    jQuery.ajax({
        type: "POST",
        url: "Report.aspx/exportToCSV",
        data: "{'elements': " + elements + ", 'headers': " + JSON.stringify(headers) + "}",
        contentType: "application/json; charset=utf-8",
        dataType: "json",

        success: function(msg) {
            if (msg.d) {
            } else {
            }
        },

        error: function(xhr, ajaxOptions, thrownError) {
            alert(xhr.statusText);
        }
    });
}

Then I use the following code on the server side, to export the retrieved data to CSV file.

/// <summary>
/// 
/// </summary>
/// <param name="elements"></param>
/// <param name="headers"></param>
[WebMethod(EnableSession=true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public static bool exportToCSV(List<object> elements, List<string> headers)
{
    try
    {
        string attachmentType = "attachment; filename=ShortageReport.csv";

        HttpContext.Current.Response.Clear();
        HttpContext.Current.Response.ClearHeaders();
        HttpContext.Current.Response.ClearContent();
        HttpContext.Current.Response.AddHeader("content-disposition", attachmentType);
        HttpContext.Current.Response.ContentType = "text/csv";
        HttpContext.Current.Response.AddHeader("Pragma", "public");

        writeHeadersInfo(headers);

        HttpContext.Current.Response.End();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

    return false;
}

But I get this expcetion: Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack.

Does anyone knows how to handle this issue?

Any help would be highly appreciated! Thanks in Advance!

~ Eder Quiñones


Solution

  • I kind of did what Leon suggested as an answer, but instead I used generic handler.

    1.- Retrieve data from the client, build json & send data to the server (Main Page):

        function dtExportToCSV(dataTable) {
            var elements = dtDataToJSON(dataTable);
            var headers = dtHeadersToJSON(tableSelector);
    
            jQuery.ajax({
                type: "POST",
                url: "ashx/Export.ashx",
                data: "{'elements': " + elements + ", 'headers': " + headers + "}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
    
                success: function(msg) {
                    window.open("Export.aspx", "Export CSV", "width=120,height=300");
                },
    
                error: function(xhr, ajaxOptions, thrownError) {
                    alert(xhr);
                }
            });
    
            return false;
        }
    

    2.- Initialize the session variable (as StringBuilder) for data inter-exchange via aspx pages (Main Page):

        protected void Page_Load(object sender, EventArgs e)
        {
            if (Session["ExportCSV"] == null)
            {
                Session["ExportCSV"] = new StringBuilder();
            }
    
            if (!IsPostBack)
            {
    
            }   
    
        }
    

    3.- Build the CSV (Generic Handler):

    public class Export : IHttpHandler, IRequiresSessionState
    {
        public void ProcessRequest(HttpContext context)
        {
            object json = null;
            byte[] input = null;
    
            JavaScriptSerializer javascriptSerializer = null;
            StringBuilder sb = null;
    
            List<object> elementList = null;
            List<string> headerList = null;
    
            try
            {
                input = readToEnd(context.Request.InputStream);
                sb = new StringBuilder();
                javascriptSerializer = new JavaScriptSerializer();
    
                foreach (byte chr in input)
                {
                    sb.Append((char)chr);
                }
    
                json = javascriptSerializer.DeserializeObject(sb.ToString());
    
                elementList = new List<object>();
                headerList = new List<string>();
    
                var dictionary = json.toType(new Dictionary<string, object>());
                foreach (KeyValuePair<string, object> keyValuePair in dictionary)
                {
                    switch (keyValuePair.Key)
                    {
                        case "elements":
                        case "ELEMENTS":
                            {
                                object[] elements = (object[])keyValuePair.Value;
                                foreach (object element in elements)
                                {
                                    elementList.Add(element);
                                }
                                break;
                            }
    
                        case "headers":
                        case "HEADERS":
                            {
                                object[] headers = (object[])keyValuePair.Value;
                                foreach (object header in headers)
                                {
                                    headerList.Add((string)header);
                                }
    
                                break;
                            }
                    }
                }
    
                ((StringBuilder) context.Session["ExportCSV"]).Append(writeBodyInfo(elementList, headerList));
                ((StringBuilder) context.Session["ExportCSV"]).Append(writeHeadersInfo(headerList));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }
    }
    

    4.- Show up the save file dialog (Export.aspx):

    public partial class Export : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                exportToCSV();
            }
        }
    
        private void exportToCSV()
        {
            Context.Response.Clear();
            Context.Response.ClearContent();
            Context.Response.ClearHeaders();
    
            Context.Response.AddHeader("Content-Disposition", "attachment;filename=ShortageReport.csv");
            Context.Response.ContentType = "text/csv";
    
            char[] separator = Environment.NewLine.ToCharArray();
            string csv = ((StringBuilder)Session["ExportCSV"]).ToString();
    
            foreach (string line in csv.Split(separator))
            {
                Context.Response.Write(line);
            }
    
            Context.Response.Flush();
            Context.Response.End();
        }
    }
    

    Any improvement, suggestion?

    ~ Eder Quiñones