Ronald
Ronald

Reputation: 1113

Return multiple files to download from asp.net

Who know how to do this? I have a loop and in that loop a List is build up. I want each List to be saved as a different file with a different filename (duh). I am using HttpContext.Current.Response for this. A single file works fine, however, I cannot loop it. The error I receive upon the second iteration is "Server cannot clear headers after HTTP headers have been sent." My code is somewhat like below. What matters of course is the part commented by // response to user. Any comments highly appreciated!

Cheerz, Ronald

        foreach (GridViewRow dr in GridView1.Rows)
        {
            // find the check box in the row
            System.Web.UI.WebControls.CheckBox cb = (System.Web.UI.WebControls.CheckBox)dr.FindControl("chkbx_selected");

            // see if it's checked
            if (cb != null && cb.Checked)
            {
                // get the cell
                DataControlFieldCell Cell = GetCellByName(dr, "GUID");
                string guid = new Guid(Cell.Text);

                // get the record filtered on the GUID
                var result_json = call_WEBAPI(guid);

                // result_json contains serialized List<string>
                List<string> result = JsonConvert.DeserializeObject<List<string>>(result_json);

                // response to user
                string filename = result[0] + ".txt";
                HttpContext.Current.Response.Clear();
                HttpContext.Current.Response.ClearHeaders();
                HttpContext.Current.Response.AppendHeader("content-disposition", string.Format("attachment; filename={0}", filename));
                HttpContext.Current.Response.ContentType = "application/octet-stream";
                foreach (string line in result)
                {
                    HttpContext.Current.Response.Write(line + Environment.NewLine);
                }

                HttpContext.Current.Response.Flush();

            }
        }

        HttpContext.Current.Response.End();

Update: I now create a zip archive in memory, create an entry and fill this with a writer. But how do I create the second entry in the .zip?? The code below does not work, error is "Entries in create mode may only be written to once, and only one entry may be held open at a time."

        using (MemoryStream zipToOpen = new MemoryStream())
        {
            using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Create, true))
            {
                ZipArchiveEntry readmeEntry = archive.CreateEntry("Readme.txt");
                using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
                {
                    writer.WriteLine("Information about this package.");
                    writer.WriteLine("========================");
                }

                List<string> result = new List<string>();

                result.Add("line1");
                result.Add("line2");

                ZipArchiveEntry readmeEntry2 = archive.CreateEntry("Readme2.txt");
                using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
                {
                    writer.Write(result);
                }

            }

            using (var fileStream = new FileStream(@"C:\test.zip", FileMode.Create))
            {
                zipToOpen.Seek(0, SeekOrigin.Begin);
                zipToOpen.CopyTo(fileStream);
            }
        }

Upvotes: 1

Views: 14202

Answers (3)

Hamit YILDIRIM
Hamit YILDIRIM

Reputation: 4539

while zip file opening it is anoying which is always open.! But if you try to close there is just one manner dispose but this time it is anoying disposed object. That is the solution Dispose and reopen all files right below one opening example

       string MappingpathZip = Path.Combine(HttpRuntime.AppDomainAppPath, @"XmlDocs\Invs\" + firmName + fileName + ".zip");
        ZipArchive zip = ZipFile.Open(MappingpathZip, ZipArchiveMode.Create);
        ZipArchiveEntry zae = zip.CreateEntryFromFile(MappingpathSignedXML, Path.GetFileName(MappingpathZip), CompressionLevel.Optimal);

        var stream = zae.Open();
        byte[] bytes;
        using (var ms = new MemoryStream())
        {
            stream.CopyTo(ms);
            bytes = ms.ToArray();
        }

Upvotes: 0

Lokki
Lokki

Reputation: 382

You can zip all files on the server and then send one .zip file. As Google does :) It'll solve you problem

Upvotes: 0

David
David

Reputation: 218808

A single response can contain only a single "file". (Technically HTTP has no concept of "files", which is kind of why this is the case. HTTP has "headers" and "content". As the error tells you, once the headers are sent you can't change them. Because they've already been sent to the client.)

You're either going to have to return the files from multiple requests/responses or compress them into a single file and return that.

(Also, why use "application/octet-stream" for a text file? Use that for the zipped file perhaps, but a text file should probably be "text/plain" or something similar.)

Upvotes: 5

Related Questions