0

I am using an async Action to download large files (>500MB). My below code hangs my application until the file download is complete.

public async Task<ActionResult> Download(string filePath, string fileName)
    {
        try
        {
            if(!string.IsNullOrEmpty(filePath))
            {
                filePath = Path.Combine(Code.Config.UploadFilesPath(), filePath);
                string contentType = MimeMapping.GetMimeMapping(fileName);

                if (System.IO.File.Exists(filePath))
                {
                    await GetLargeFile(filePath, fileName);
                }
                else
                {
                    return Content("Requested File does not exist");
                }
            }
        }
        catch(Exception ex) { }
        return Content("");
    }

private async Task GetLargeFile(string fullPath, string outFileName)
{
  System.IO.Stream iStream = null;

        // Buffer to read 10K bytes in chunk:
        byte[] buffer = new Byte[10000];

        // Length of the file:
        int length;

        // Total bytes to read:
        long dataToRead;

        // Identify the file to download including its path.
        string filepath = fullPath;

        // Identify the file name.
        string filename = System.IO.Path.GetFileName(filepath);

        try
        {
            // Open the file.
            iStream = new System.IO.FileStream(filepath, System.IO.FileMode.Open,
                        System.IO.FileAccess.Read, System.IO.FileShare.Read);


            // Total bytes to read:
            dataToRead = iStream.Length;

            Response.Clear();
            Response.ContentType = MimeMapping.GetMimeMapping(filename);
            Response.AddHeader("content-disposition", "attachment; filename=\"" + outFileName + "\"");
            Response.AddHeader("Content-Length", iStream.Length.ToString());

            // Read the bytes.
            while (dataToRead > 0)
            {
                // Verify that the client is connected.
                if (Response.IsClientConnected)
                {
                    // Read the data in buffer.
                    length = iStream.Read(buffer, 0, 10000);

                    // Write the data to the current output stream.
                    Response.OutputStream.Write(buffer, 0, length);

                    // Flush the data to the output.
                    Response.Flush();

                    buffer = new Byte[10000];
                    dataToRead = dataToRead - length;
                }
                else
                {
                    //prevent infinite loop if user disconnects
                    dataToRead = -1;
                }
            }
        }
        catch (Exception ex)
        {
            throw new ApplicationException(ex.Message);
        }
        finally
        {
            if (iStream != null)
            {
                //Close the file.
                iStream.Close();
            }
            Response.Close();
        }
}

It is strange that if I use the same code as above on a brand new Web application and download a large file (around 1GB), that application doesn't hang as I can switch between Views & trigger javascript alert function during download process.

But with my real project, application hangs until download is completed unfortunately.

Is there anything that I should consider in configs or anything else?

Any debugging tips to see how it is working on a brand new application but not on my actual project?

Raghu
  • 11
  • 3
  • 1
    You need to post the code of GetLargeFile if you want any help – Camilo Terevinto May 23 '17 at 11:45
  • `Some code to read file & write it to response stream in loop` Using async IO? Show us the code. – spender May 23 '17 at 11:59
  • Thanks for your replies. Code updated – Raghu May 23 '17 at 12:10
  • 1
    You aren't doing anything async in your async method. Simply marking a method as `async` will do **absolutely nothing** if the async method never awaits, and the method will run synchronously. This is bad juju on a webserver. Use the Task based async methods with `await` for all your IO interactions. – spender May 23 '17 at 12:12
  • I am a newbie to the async world. Do you mind updating 'GetLargeFile' method if that doesn't take your time please? – Raghu May 23 '17 at 12:16
  • 2
    @Raghu Why don't you give it a shot yourself and if you run into more trouble, ask a new question? To get you started, your first read operation might be `length = await iStream.ReadAsync(buffer, 0, 10000);`. – spender May 23 '17 at 12:21
  • Thank you. will give it a try with that tip in mind :) – Raghu May 23 '17 at 12:33
  • @spender: Thanks for your inputs. I am currently stuck at making 'Response.Flush()' as async, looks like this is blocking/hanging my application after making its above lines async. Any ideas how to make it asyn or any other better way? – Raghu May 23 '17 at 13:22
  • google it [FlushAsync](https://msdn.microsoft.com/en-us/library/hh193384(v=vs.110).aspx) – bradgonesurfing May 24 '17 at 14:05

1 Answers1

0

In the context of a web application, async merely allows the thread serving the request to be returned to the thread pool, when it enters a wait-state. If the thread is actively doing work, such as in spooling a file into the response, then it's effectively the same as running sync.

Again, in the context of a web application, there's a request and a response. The client makes a request and the server responds to that request. Here, that response is a file, so async or not, the thread will be tied up until the response is complete. You cannot "return" from the action until the response has been sent.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444