3

I've spent the last couple days attempting to address this issue. I've performed exhaustive Google searches, tried many solutions, with no luck.

In essence, I'm trying to export data to a CSV and download it. The code works, but throws an exception. My particular usage requires for multiple downloads (like the example below) to occur back-to-back, but the exception prevents this from happening.

string attachment = string.Format("attachment; filename={0}_OutPut.csv", companyName);

HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.AddHeader("content-disposition", attachment);
HttpContext.Current.Response.ContentType = "text/csv";
HttpContext.Current.Response.AddHeader("Pragma", "public");
WriteColumnName("Column1,Column2,Column3");

StringBuilder stringBuilder = new StringBuilder();

foreach (Item i in list.Items)
{
    AddData(i.TaxCodeValue.ToString(), stringBuilder);
    AddData(i.Description.ToString(), stringBuilder);
    AddData(i.Description.ToString(), stringBuilder);

    stringBuilder.Length = 0; //Reset Stringbuilder value
    HttpContext.Current.Response.Write(stringBuilder.ToString());
    HttpContext.Current.Response.Write(Environment.NewLine);
}

HttpContext.Current.Response.End(); <---This is the problem

I've read:

http://support.microsoft.com/kb/312629

Alternative to Response.End()?

Thread was being aborted when exporting to excel?

I understand Response.End() is throwing an exception because it is aborting the thread (generally not advised). However, for my scenario, I don't see any alternative.

A few of the things I've tried:

  1. HttpContext.Current.ApplicationInstance.CompleteRequest(); as an alternative to Response.End(). It results in my CSV being filled with garbage, and not the actual data I'm writing.
  2. HttpContext.Current.Response.Redirect("Page.aspx", false); just refreshes the page, and the written data is not downloaded.
  3. Catching the exception within a catch block. It always manages to escape and ripple up.

Any ideas?

Thank you in advance.

Community
  • 1
  • 1
Sc0rpio
  • 189
  • 2
  • 4
  • 12
  • What do you mean multiple downloads? One file with many records, or do you mean one file after the other? – Oded Jul 10 '12 at 21:06
  • One file downloaded after the other (in other words, the code above is repeated a few times in my actual implementation for different files). – Sc0rpio Jul 10 '12 at 21:13
  • Did you try `Response.Flush()` before `Response.End()`? I am not sure you _can_ send multiple files in a single response, by the way. – Oded Jul 10 '12 at 21:14
  • Yes, I did. I'm not trying to send multiple files in a single response- just want multiple files to download, each in their own response. – Sc0rpio Jul 10 '12 at 21:24
  • Where do `companyName` and `list` come from? Some dynamic data on the page, or something that can be determined via query string parameters? I ask because you should be using a generic handler instead of a page object for this type of operation. – Mike Guthrie Aug 29 '12 at 19:35

1 Answers1

0

When you output a file to the stream as you are doing here, you essentially have to "hijack" the normal flow of a Page. You should instead use a generic handler (an .ashx file implementing IHttpHandler).

When implementing the generic handler, you don't need to worry about calling Response.Clear and Response.End because you will be supplying the full response yourself. Then your page would have links to the ashx handler, passing in necessary Query String parameters to load your data.

Example: Your page would have a link to "/MyCsvDownload.ashx?CompanyID=11"

Your project would have a HelperClass defined declaring WriteColumnHeaders and AddData static methods, and a MyCsvDownload.ashx with code like so:

public class MyCsvDownload : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var companyId = int.Parse(context.Request.QueryString["CompanyID"]);

        string companyName;
        ItemList list;

        // TODO: Load company name and items from Company ID.

        string attachment = string.Format("attachment; filename={0}_OutPut.csv", companyName); 

        context.Response.AddHeader("content-disposition", attachment);
        context.Response.ContentType = "text/csv";
        context.Response.AddHeader("Pragma", "public");

        StringBuilder stringBuilder = new StringBuilder();

        HelperClass.WriteColumnName("Column1,Column2,Column3", stringBuilder);
        context.Response.Write(stringBuilder.ToString());
        context.Response.Write(Environment.NewLine);
        stringBuilder.Clear();

        foreach (Item i in list.Items)
        {
            HelperClass.AddData(i.TaxCodeValue.ToString(), stringBuilder);
            HelperClass.AddData(i.Description.ToString(), stringBuilder);
            HelperClass.AddData(i.Description.ToString(), stringBuilder);

            context.Response.Write(stringBuilder.ToString());
            context.Response.Write(Environment.NewLine);
            stringBuilder.Clear();
        }

        context.Response.Flush();
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}
Mike Guthrie
  • 4,029
  • 2
  • 25
  • 48