Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Some questions about writing on ASP.NET response stream

I'm making tests with ASP.NET HttpHandler for download a file writting directly on the response stream, and I'm not pretty sure about the way I'm doing it. This is a example method, in the future the file could be stored in a BLOB in the database:

        public void GetFile(HttpResponse response)
    {
        String fileName = "example.iso";
        response.ClearHeaders();
        response.ClearContent();
        response.ContentType = "application/octet-stream";
        response.AppendHeader("Content-Disposition", "attachment; filename=" + fileName);
        using (FileStream fs = new FileStream(Path.Combine(HttpContext.Current.Server.MapPath("~/App_Data"), fileName), FileMode.Open))
        {
            Byte[] buffer = new Byte[4096];
            Int32 readed = 0;

            while ((readed = fs.Read(buffer, 0, buffer.Length)) > 0)
            {
                response.OutputStream.Write(buffer, 0, readed);
                response.Flush();
            }
        }
    }

But, I'm not sure if this is correct or there is a better way to do it. My questions are:

  1. When I open the url with the browser, appears the "Save File" dialog... but it seems like the server has started already to push data into the stream before I click "Save", is that normal?
  2. If I remove the line"response.Flush()", when I open the url with the browser, ... I see how the web server is pushing data but the "Save File" dialog doesn't come up, (or at least not in a reasonable time fashion) why?
  3. When I open the url with a WebRequest object, I see that the HttpResponse.ContentLength is "-1", although I can read the stream and get the file. What is the meaning of -1? When is HttpResponse.ContentLength going to show the length of the response? For example, I have a method that retrieves a big xml compresed with deflate as a binary stream, but in that case... when I access it with a WebRequest, in the HttpResponse I can actually see the ContentLength with the length of the stream, why?
  4. What is the optimal length for the Byte[] array that I use as buffer for optimal performance in a web server? I've read that is between 4K and 8K... but which factors should I consider to make the correct decision.
  5. Does this method bloat the IIS or client memory usage? or is it actually buffering the transference correctly?

Sorry for so many questions, I'm pretty new in web development :P

Cheers.

like image 720
vtortola Avatar asked Apr 02 '10 21:04

vtortola


3 Answers

  1. Yes; this is normal.
  2. If you never flush, the browser doesn't get any response until the server finishes (Not even the Content-Disposition header). Therefore, it doesn't know to show a file dialog.
  3. The Content-Length header only gets set if the entire response is buffered (If you never flush) or if you set it yourself. In this case, you can and should set it yourself; write

    response.AppendHeader("Content-Length", new FileInfo(path).Length.ToString());
    
  4. I recommend 4K; I don't have any hard basis for the recommendation.
  5. This method is the best way to do it. By calling Flush inside the loop, you are sending the response down the wire immediately, without any buffering. However, for added performance, you can use GZIP compression.
like image 88
SLaks Avatar answered Nov 15 '22 12:11

SLaks


  1. Yes, it is buffering.
  2. Flush pushes the cached content to the browser. If it is never pushed, you won't get a save dialog box.
  3. Hard to tell without seeing the exact files/URLs/Streams you are using.
  4. I think the factors depends on how sluggish your page is, really. You will have better performance toward 4k. And perhaps, the lower value will be better to accommodate slower connections.
  5. See #1 & 2.
like image 29
TheGeekYouNeed Avatar answered Nov 15 '22 13:11

TheGeekYouNeed


For #3 you need to set the content-length header in your http-response. Many of those values come from http headers.

I believe you can change the bufferring by changing a buffering property on the response object to false. Haven't done it in a while so I don't remember what it might be.

like image 25
Min Avatar answered Nov 15 '22 12:11

Min