views:

165

answers:

1

Hi there,

I'm using ASP.NET 4, EF 4 and FILESTREAM in SQL 2008 to add/read files to the DB. I'm able to upload files just fine, but I'm not able to retrieve the files the way I want to. Here's what I'm doing -

  1. I have a grid which displays a list of files. Each line item is a file with a CommandName and CommandArgument set.
  2. The user clicks on any one of these files and I capture the event in the RowCommand event. At this point, I get the ID of the file and retrieve the byte[] from the DB. How do I display a standard download box for the user to Save or Cancel the download?

I can write the stream to a temp file (using System.IO.File.WriteAllBytes) and then do a Server.Transfer to the temp file but I think it's an unnecessary step. Is there a way, I can read the bytes directly to memory, then set the ContentType/MimeType and allow the user to save the file?

I understand that accessing the FILESTREAM file using T-SQL is slower than accessing the same using WIN32 API's (using a combination of the new SystemFile.PathName() & Impersonation) but EF4 makes working with SQL faster so I'm going that route.

Here's the code -

var file = db.Storage.Single(f => f.ID.Equals(fileID)); // fileID is set in CommandArgument
// file.Contents has the byte [].
// display a  standard file download box.

Thanks for any help!

A: 

You probably want to create an .ashx hanlder (assuming you are using asp) and to a Server.Transfer() or Server.Redirect() to that URL.

Alternatively, you could change the GridView to display a hyperlink to the handler URL as well.

Right click in Solution Explorer > New Item > ASP.Net Hanlder.

Code for handler:

public class DownloadFile : IHttpHandler { // add IRequiresSessionState if needed 
  public bool IsReusable { get { return true; } }

  public void ProcessRequest(HttpContext context) {
    var fileID = context.Request.QueryString["fileID"]; // assuming fileID is a string
    var file = db.Storage.Single(f => f.ID.Equals(fileID)); 
    // set the content type
    context.Response.OutputStream.Write(file.Contents, 0, file.Contents.Length);
  }
}

You probably want to add some error checking as well.

Your URL would be something like this: /DownloadFile.ashx?fileID=somefileid

Zachary Yates
Yes, I'm using ASP.NET (updated the question with this info). What URL are you talking about? All I have is bytes. Do you want me to write the bytes to a file and do a redirect to that file? If so, I'm trying to avoid that route as I think it may be necessary.
No need to write it to a file, you can write it directly to the OutputStream. I added code to better illustrate the point.
Zachary Yates
I think I see what you're saying. The link will look something like /GetFile.ashx?id={ID}, and the handler will read the bytes from the DB and write the bytes to the response stream directly without saving to disk?
Thanks for the code sample, I'll give it a shot and report back.
Exactly. Should also make your grid a little simpler.
Zachary Yates
It worked perfectly. Thank you!
Quick question - why can't I do Response.OutputStream.Write in my aspx itself? Why do I have to have a separate .ashx? Gives me a "The message received from the server could not be parsed." exception when I try to do that.
Do you mean it gives you a parsing error when you use Response.OutputStream.Write() in your .aspx? Most likely because there is already something already written to the output stream when you call .Write() in the aspx. I prefer the handler just because it separates the concerns of sending a content file to the browser (from the ashx) and displaying html (from your aspx)
Zachary Yates