Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Securing Large Downloads Using C# and IIS 7

Here's the setup:

  • 1 web server running a C# app to which my users (stored in a MySQL database on said server) authenticate.

  • 1 file server running software TBD. In the past I've used lighttpd and mod_secdownload to secure the files on the file servers, and it's worked well(ish).

I'm wondering if there is a way to do this using a combination of IIS and C# .Net. All my other servers are running that combo, and it would simplify things a bit if I could do the same for the file servers. The kicker is, the files that are being hosted are large. I've seen examples of people using a small app to create a FileStream object, read in the file, and create the HTTP Response by hand. This works, but since I'm working with files 500+ MB in size, it's slow as heck. And I'll potentially have 300 users hitting the box at once, requesting files. That's no good.

So, anyone see a way around this? I'm trying to create a more transparent system, and if all my servers are running the same software/hardware, it will make my life a whole lot simpler. Thanks in advance for any advice you give!

like image 288
Stephen Fischer Avatar asked Feb 01 '10 18:02

Stephen Fischer


2 Answers

You know what? The KB article is poo. Here is my official recommendation:

public void StreamFile(string filePath)
{
    string fileName = Path.GetFileName(filePath);

    using (var fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        var contentLength = fStream.Length;

        if (Request.UserAgent.Contains("MSIE"))
        {
            Response.AddHeader("Content-Transfer-Encoding", "binary");
        }

        Response.ContentType = "application/octet-stream";
        Response.AddHeader("Content-Length", contentLength.ToString());

        // Even though "Content-Disposition" should have an upper-case "d", as per http://www.ietf.org/rfc/rfc2183.txt
        // IE fails to recognize this if the "d" is upper-cased.
        Response.AddHeader("Content-disposition", "attachment; filename=" + fileName);

        var buffer = new byte[8192];

        while (Response.IsClientConnected)
        {
            var count = fStream.Read(buffer, 0, buffer.Length);
            if (count == 0)
            {
                break;
            }

            Response.OutputStream.Write(buffer, 0, count);
            Response.Flush();
        }
    }

    Response.Close();
}
like image 78
John Gietzen Avatar answered Oct 29 '22 09:10

John Gietzen


This thread has my solution to keep memory usage down while users are downloading files. You probably want a bigger buffer than my sample uses, though.

like image 20
Ray Avatar answered Oct 29 '22 07:10

Ray