views:

1755

answers:

4

I have a Stream object that is populated with the contents of an XSD file I have as an embedded resource on a project I am working on like so:

using ( Stream xsdStream = assembly.GetManifestResourceStream( xsdFile ) )
{
  // Save the contents of the xsdStream here...
}

Within this using block I would like to prompt the user with a Save File dialog on the web where they can choose to save off this XSD file contained within the stream.

What is the best way to accomplish this? I am completely lost and can't seem to Google the right terms to get a relevant answer.

Thanks!

A: 

If you aren't using AJAX, you can use Response.WriteFile. Else I'd use a MemoryStream. That's how I did it here. Sorry it's in VB.NET, I haven't transcoded it. Note this also lets you download a file THROUGH the webserver, i.e. if your file is on an app server w/o public access.

Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.Sql
Imports System.Net
Imports System.IO

Partial Class DownloadFile
Inherits System.Web.UI.Page

Protected Sub page_load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim url As String = Request.QueryString("DownloadUrl")
If url Is Nothing Or url.Length = 0 Then Exit Sub

'Initialize the input stream
Dim req As HttpWebRequest = WebRequest.Create(url)
Dim resp As HttpWebResponse = req.GetResponse()
Dim bufferSize As Integer = 1 

'Initialize the output stream
Response.Clear()
Response.AppendHeader("Content-Disposition:", "attachment; filename=download.zip")
Response.AppendHeader("Content-Length", resp.ContentLength.ToString)
Response.ContentType = "application/download"

'Populate the output stream
Dim ByteBuffer As Byte() = New Byte(bufferSize) {}
Dim ms As MemoryStream = New MemoryStream(ByteBuffer, True)
Dim rs As Stream = req.GetResponse.GetResponseStream()
Dim bytes() As Byte = New Byte(bufferSize) {}
While rs.Read(ByteBuffer, 0, ByteBuffer.Length) > 0
Response.BinaryWrite(ms.ToArray())
Response.Flush()
End While

'Cleanup
Response.End()
ms.Close()
ms.Dispose()
rs.Dispose()
ByteBuffer = Nothing
End Sub
End Class
tsilb
A: 

It sounds to me that you should take a look at the WebResource.axd handler.

Microsoft have an excellent article on the subject.

Edit:

It seems that tsilb beat my answer by a minute or so. However, the AssemblyResourceLoader (aka. WebResource.axd) is already implemented to do this for you and to do it properly, and don't forget that this puppy supports output caching. So go ahead use that instead and spare yourself the trouble. ;)

JohannesH
+1  A: 

You will want to set the content-disposition:

Response.AddHeader "Content-Disposition","attachment; filename=" & xsdFile

You will also want to set the Content-Type to text/plain and Content-Length to the size of the file. Then you write the contents of the file.

SmokingRope
While this answer does provide valuable information, that is if Rob wanted to roll his own streaming, it doesn't answer the question about how to stream the file in the first place. If Rob wanted to go that way, I think tsilb has a better answer. However I still think my way is a lot easier... ;)
JohannesH
A: 
private void DownloadEmbeddedResource( 
  string resourceName, Assembly resourceAssembly, string downloadFileName )
{
  using ( Stream stream = resourceAssembly.GetManifestResourceStream( resourceName ) )
  {
    if ( stream != null )
    {
      Response.Clear();
      string headerValue = string.Format( "attachment; filename={0}", downloadFileName );
      Response.AppendHeader( "Content-Disposition:", headerValue );
      Response.AppendHeader( "Content-Length", stream.Length.ToString() );
      Response.ContentType = "text/xml";

      var byteBuffer = new Byte[1];

      using ( var memoryStream = new MemoryStream( byteBuffer, true ) )
      {
        while ( stream.Read( byteBuffer, 0, byteBuffer.Length ) > 0 )
        {
          Response.BinaryWrite( memoryStream.ToArray() );
          Response.Flush();
        }
      }

      Response.End();
    }
  }
}

I ended up using this method above. Thank you for helping me with the syntax tsilb. JohannesH, I would have used your recommendation if the resource wasn't already coming from a different assembly (Sorry, I should have cleared that up in my original question).

The code above works but I am encountering a fairly weird problem... After the method completes and the download finishes, the page is still never seems to come back to life and the mouse is still in hourglass mode like it still thinks work is being done. Any idea on how to remedy that?

Thanks again for all your help!