Detect when file download request is finished with jQuery

Figuring out when file download request completes with jQuery

Recently I started a small free service app for extracting icons from application files .exe and .dll. It is called IconExtract and it is hosted at http://iconextract.dejanstojanovic.net/

It basically takes application file you submit, extracts icons and returns them zipped, so beside long running upload there is some processing on the back-end when file is uploaded as well.

The app was working fine on local machine but, when it became live I noticed that upload is a lot slower than when I tested locally, which is normal :).

The problem was that when you start upload it takes even few minutes, and you stare at status form while that happens. I might also make user thing that nothing is happening, so he can try to to resubmit the same file multiple times by mistake.

To make this more user friendly I added gif animation which is shown while uploading and processing is happening. This worked fine, but after file is generated and sent back to browser from server side, it was still showing animation, which looked weird.

Iconextract Flow

I started looking for a solution how to detect that file is generated from back-end and sent back to the browser. After some time spent googling I run across different approaches for this and found idea how to determine that download request is finished.

The solution is based on cookies. Simply before submit a loop starts which check for some cookie value. While cookie value is empty, it starts new loop iteration. When it is set it jums out of the loop and hides the progress animation.

How ever, there is a catch as user can open multiple windows and start upload in them. That means you will need some unique key for each window.

There are two ways of generating GUID:

  • Client side with jQuery
  • Server side (depending on your back-end technology used)

Although generating GUID with jQuery is easy and you can find bunch of functions out there that do that like one in this article, I decided to do it from the back-end using Razor as my app is an ASP.NET MVC app.

function guid() {
  function s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
      .toString(16)
      .substring(1);
  }
  return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
    s4() + '-' + s4() + s4() + s4();
}
    
@Guid.NewGuid().ToString()
    

As the solution I used for detecting that long running request was finished relies on cookie values I needed access to cookies on both client ans server side. On server side it is pretty easy to use it out of the box in pretty much any environment, but for client side, I decide to use jquery.cookie plugin to make things easier.

The following diagram shows the steps and actions for both client and server side:

Downalod Loading

On the client side this looks like the following

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=windows-1250">
  <script src="//code.jquery.com/jquery-1.11.0.min.js" defer></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js" defer></script>
  <title></title>
  </head>
  <body>
        <form action="/postfile" method="POST">
            <input type="file" name="postedfile" />
            <input type="hidden" name="windowid" value="@Guid.NewGuid().ToString()" />
            <button type="submit">Upload file</button>
        </form>
        
        <script type="text/javascript">
             $("form").submit(function () {
                 $(".working").show();
                 $(".input-form").hide();
                 checkCookie();
             });
             
             function checkCookie() {
                var cookieVal = $.cookie($("input[name='windowid']").val());
                if (cookieVal == null || cookieVal === 'undefined') {
                    setTimeout("checkCookie();", 1000);
                }
                else {
                    $(".working").hide();
                    $(".input-form").show();
                    $("form")[0].reset();
                }
            }
        </script>
  </body>
</html>
    

The loop steps are executed on every 1 second after submit an they check whether cookie value is set from the back-end, which automatically means that upload and processing is done from the back-end and it is time to show input form again.

While on the server side, beside your logic of generating the response, you need to add one more line before exiting the cod which will set some value to cookie with name sent in windowid hidden field from client side.

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Extract(HttpPostedFileBase file, string windowid)
        {
            /* File processing logic here */
            Response.Cookies.Add(new HttpCookie(windowid, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss:ff")));
			
            /* Return file result*/
            return new FileStreamResult(stream, "application/x-zip-compressed")
            {
                FileDownloadName = string.Format("result.zip")
            };
}

    

This solutions works well with IconExtract web app for now and hopefully if you have some case like this, this should work for you too.

 

 

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform including ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

.NET

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article