0

I have a table named Logs that has different records stored based on the Machine1 column,

** I am trying to achieve**

To export Today's records for a specific Machine1 column, so far this snippet works for once, but for the second iteration is throws the exception “server cannot append header after http headers have been sent”

** I read alot about it**

In simple words; "one request for one response" What I came across next was DotNetZip

** I dont know how do I add the files to zip?**

My original func:

  public void ExportDailyCSV(/*string machine*/)
        {

            var machineList = db.Machines.ToList();
            foreach (var mk in machineList)
            {

                var sb = new StringBuilder();
                var list = (from o in db.Logs
                            where o.Machine == mk.Machine1 && o.sDate == DateTime.Today
                            select o).ToList();
                var fileName = mk.Machine1;
        sb.AppendFormat("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10}", "\"Ac-No\"", "\"Name\"", "\"sTime\"", "\"Verify Mode\"", "\"Machine\"", "\"Exception\"", "\"checktype\"", "\"sensorid\"", "\"workcode\"", "\"sDate\"", Environment.NewLine);
                foreach (var item in list)
                {
                    sb.AppendFormat("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10}", "\"" + item.Ac_No + "\"", "\"" + item.Name + "\"", "\"" + item.sTime + "\"", "\"" + item.VerifyMode + "\"", "\"" + item.Machine + "\"", "\"" + item.Exception + "\"", "\"" + item.CheckType + "\"", "\"" + item.SensorId + "\"", "\"" + item.WorkCode + "\"", "\"" + item.sDate.Value.ToShortDateString() + "\"", Environment.NewLine);
                }

                try
                {
                    if (System.Web.HttpContext.Current != null)
                    {
                        //Get Current Response  
                        var response = System.Web.HttpContext.Current.Response;
                        response.BufferOutput = true;
                        response.Clear();
                        response.ClearContent();
                        response.ClearHeaders();
                        response.ContentEncoding = Encoding.Unicode;
                        response.ContentEncoding = Encoding.Default;
                        var attachmentValue = string.Format("attachment;filename={0}.csv", fileName);
                        response.AddHeader("content-disposition", attachmentValue);
                        response.ContentType = "text/csv";
                        response.Write(sb.ToString());
                        response.End();
                    }

                }
                catch (ArgumentNullException ex)
                {
                    Response.Write("Property: " + ex.ParamName + " Error: " + ex.Message);
                }
                Task.Delay(300000).ContinueWith(t => ExportDailyCSV());
        }
    }

DotNetZip sample:

uisng (ZipFile zip = new ZipFile())
 {
     // add this map file into the "images" directory in the zip archive
     zip.AddFile("c:\\images\\personal\\7440-N49th.png", "images");
     // add the report into a different directory in the archive
     zip.AddFile("c:\\Reports\\2008-Regional-Sales-Report.pdf", "files");
     zip.AddFile("ReadMe.txt");
     zip.Save("MyZipFile.zip");
 }

How do I merge them both? or use the DotNetZip for multiple calls as in my original func?

1 Answers1

0

If your goal is to just compile the CSV and return it as a download, you can first completely read all the data, build the CSV and then return it as one stream response. The browser and web server will then handle the rest (as in the actual streamed download).

For example:

public class LogsController : ApiController
{
    public HttpResponseMessage Get()
    {
        var machineList = db.Machines.ToList();
        var sb = new StringBuilder();

        foreach (var mk in machineList)
        {

            var list = (from o in db.Logs
                        where o.Machine == mk.Machine1 && o.sDate == DateTime.Today
                        select o).ToList();
            var fileName = mk.Machine1;
    sb.AppendFormat("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10}", "\"Ac-No\"", "\"Name\"", "\"sTime\"", "\"Verify Mode\"", "\"Machine\"", "\"Exception\"", "\"checktype\"", "\"sensorid\"", "\"workcode\"", "\"sDate\"", Environment.NewLine);
            foreach (var item in list)
            {
                sb.AppendFormat("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10}", "\"" + item.Ac_No + "\"", "\"" + item.Name + "\"", "\"" + item.sTime + "\"", "\"" + item.VerifyMode + "\"", "\"" + item.Machine + "\"", "\"" + item.Exception + "\"", "\"" + item.CheckType + "\"", "\"" + item.SensorId + "\"", "\"" + item.WorkCode + "\"", "\"" + item.sDate.Value.ToShortDateString() + "\"", Environment.NewLine);
            }
        }

        HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);

        var stream = new MemoryStream(Encoding.ASCII.GetBytes(sb.ToString()));

        result.Content = new StreamContent(stream);
        result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
        result.Content.Headers.ContentDisposition.FileName = filename;

        return result;
    }

}

This presumes the files are small enough to be loaded into the web server's memory for every request. If the files are very large, you can always write them to disk and then return a standard file stream as opposed to the memory stream in the example. Just remember to clean up the file when done (with a finally block for instance).

K. Wood
  • 29
  • 3