jason
jason

Reputation: 7164

How to send multiple files with Response. ASP.NET

I'm trying to call below code in a loop hundreds of times:

   Sub ExportReport(ByVal en As MyReport)
        Dim warnings As Warning() = Nothing
        Dim streamids As String() = Nothing
        Dim mimeType As String = Nothing
        Dim encoding As String = Nothing
        Dim extension As String = Nothing
        Dim bytes As Byte()
            bytes = aReport.ServerReport.Render("WORD", Nothing, mimeType, encoding, extension, streamids, warnings)


        Response.Buffer = True
        Response.Clear()
        Response.ContentType = mimeType
        Response.AddHeader("content-disposition", "attachment; filename=" & en.ToString() & "." + extension)
        Response.BinaryWrite(bytes)
        Response.Flush()
        Response.End()

    End Sub

And I'm getting this error :

Server cannot append header after HTTP headers have been sent.

How can I change the code so that I can loop this piece of code? Thanks.

EDIT : I added this line after Response.End()

Response.Redirect(Request.Url.AbsoluteUri)

And I get this error :

Cannot redirect after HTTP headers have been sent.

Upvotes: 1

Views: 4938

Answers (2)

Sunil
Sunil

Reputation: 3424

WWW works on a request / response mechanism. For every request there is only 1 response. You cannot change that basic mechanism. When browser sends a request it is expecting one and only one response. So if it receives more than 1 response, it either issues a warning to the user to block this behaviour or may choose to ignore the extra responses by itself. Thus these extra responses may be lost.

Having said that you have 2 options with you:

  1. Zip all the files that you want to download and download as a single file.

You can use Popular framework Ionic.Zip.

First, keep all your files in a local directory on the server. Then use this library to zip the entire folder.

Pseudo code:

Imports (var zip = New Ionic.Zip.ZipFile())
{
   zip.AddDirectory("DirectoryOnDisk", "rootInZipFile")
   Response.Clear()
   Response.AddHeader("Content-Disposition", "attachment; filename=DownloadedFile.zip")
   Response.ContentType = "application/zip"
   zip.Save(Response.OutputStream)
   Response.End()
}
  1. Add a mechanism to issue multiple request using Javascript to get multiple responses, so browser still treats this behaviour as normal.

Upvotes: 1

Andrew Morton
Andrew Morton

Reputation: 25013

A normal web page will have a load of (headers) stuff set up for you already, but you don't want any of that: you want complete control over what is sent to the browser. If you cause a redirect to something which sends the headers shown in code later here, the browser will (normally) download the data.

In the code-behind you can have something like

Protected Sub btn_click(ByVal sender As Object, ByVal e As EventArgs) Handles btn.Click
    Response.Redirect("~/sendfile.ashx?ref=" & enReference, False)
    Context.ApplicationInstance.CompleteRequest()

End Sub

You will also need to add a generic handler (right-click on the project in Solution Explorer, Add->New Item... -> Visual Basic--Web--General choose "Generic Handler"; give it a name like sendfile.ashx) which is somewhat like

Imports System.IO

Public Class sendfile
    Implements System.Web.IHttpHandler, IReadOnlySessionState

    Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest

        Dim enReference = context.Request.QueryString("ref")
        ' do whatever is needed to get the report from enReference '
        Dim bytes As Byte() = aReport.ServerReport.Render("WORD", Nothing, mimeType, encoding, extension, streamids, warnings)
        Dim downloadName = yourfilename & "." & yourextension

        context.Response.ContentType = "application/octet-stream"
        context.Response.AddHeader("content-disposition", "attachment; filename=""" & downloadName & """"  )
        context.Response.BinaryWrite(bytes)

    End Sub

    ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

End Class

and you will need to work out the code to create the data to be sent.

If you don't need to use session state then you can remove the , IReadOnlySessionState part on the Implements line.

You might need to add context.Response.Flush(). If you find that the response does not have a Content-Length header, then you ought to add one so that the browser can show a meaningful download progress.

Upvotes: 0

Related Questions