views:

225

answers:

2

I have a set of pdf files stored in the location accessible only by the application - so those files cannot be accessed directly via http.

The file paths are stored by the database and when the user is given the option to download a file the code as below is being executed:

 Response.ContentType = "Application/pdf"
 Response.AppendHeader("Content-Disposition", "attachment; filename=<some file name>")
 Response.TransmitFile(Server.MapPath("<the file path>"))
 Response.End()

It all works fine provided I am dealing with a single file

The problem:

I have a stored procedure that returns a list of all the files available to the user for download, and it can return any number of files ranging from 1 to 20.

I would like to create a page that would list all those files along with the download option next to each file.

Something like

File name 1, some description 1, download (button, link?)
File name 2, some description 2, download (button, link?)

It cannot be simply an url link because as I mentioned the files are not directly accessible and some code needs to be executed for each download

What would an elegant solution to accomplish this using .net2.0?

+7  A: 

With regular ASP.NET (2.0) you would want to create a "handler". The easiest option is to add a "generic handler" (ashx) template - then put a regular link on the page to (for example) "download.ashx?id=2".

Inside the handler, parse the query-string, and stream the file to the user:

    public void ProcessRequest(HttpContext context)
    {
        // set headers...
        string filePath = FindPath(context.Request.QueryString("id"));
        context.Response.WriteFile(filePath);
    }

where FindPath resolves the physical file from the query-string link.

If this was ASP.NET MVC, you could just use the File(path) action-result.

Marc Gravell
+1 - Very nice answer. Very few people know how to use ASHX files despite their usefulness.
Mark Brittingham
+1. Thanks Mark, that is pointing me into the right direction. I suppose the FindPath function result would need be user/session specific to make ensure that someone does not call the page directly with the ID that he has no permissions for.
kristof
Personally I'd have fixed ids, and just check the permissions inside the method (perhaps using database ids). That way links work between sessions/browsers, as long as you have access.
Marc Gravell
That was more or less what I meant by saying session/user specific. I was either considered storing list of file paths is session and the id would be the id in the list, or alternatively to retrieve it from db using ID passed but then I need to also check if the user has the permissions for file
kristof
To check that i would need to pass userId as well, in session I believe otherwise you can call directly Download.ashx?id=2?user=5. I understand that to use the session data the handler needs to implement System.Web.SessionState.IRequiresSessionState.
kristof
But perhaps I make it to complicated
kristof
You don't need session state to identify a user...?
Marc Gravell
A: 

The URLs in the list of files your return to the user can point to an ASPX page that can download them just like in the example code you provided.

Assaf Lavie