Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Show an animation while waiting for a download to start

0.00/5 (No votes)
23 Apr 2016 1  
This article shows a cool trick to display a loading animation while waiting for a download to start. If the download starts, the animation is automatically hidden.

Introduction

In a web application you often want to download a file that is created on the fly on the server. If this takes more than a few seconds you want to show some kind of feedback to the user, like a "Please wait..." message and a nice loading animation:

It is easy to show such an animation using JavaScript when a download link or button is clicked and the request is sent back to the server. However when the server answers directly with the file to download, there is no way to hook into that event and hide the animation.

I will show you a nice trick to hide the animation as soon as the download is ready and the browser shows the download dialog.

Background

The trick I use makes use of a cookie: Just before the server starts writing the generated file into the response stream, it sets a cookie. The cookie is sent to the browser as part of the HTTP header of the response and is immediately available as soon as the first bytes of the response are received by the browser.

Cookies can also be read from JavaScript. So what we do is starting a timer that polls for the existence of this cookie when the download link/button is pressed. This timer continues to work while the request is running. As soon as we have found our cookie, we know that the download is ready and can hide the animation.

Using the code

Let's take a look at the client code first. This is the MVC view:

C#
@using (Html.BeginForm("Download", "Home"))
{
    @Html.Hidden("cookieValue")
    <input type="submit" id="btnDownload" value="Download!" />
}

We have a form that will issue an HTTP POST and call an action "Download" in a controller named "Home". Within the form we have a hidden field and a button that starts our download.

JavaScript
<script>
    var _tmr;
    $(function () {
        $('#btnDownload').click(function () {
            // save timestamp in hidden field
            $('#cookieValue').val(Date.now().toString());

            // show animation
            $('body').addClass("loading");

            // start timer to wait for cookie
            _tmr = window.setInterval(function () {
                var _str = 'dlc=' + $('#cookieValue').val();
                if (document.cookie.indexOf(_str) !== -1) {
                    // hide animation
                    $('body').removeClass("loading");
                }
            }, 100);
        });
    });
</script>

Using jQuery, we attach a click event to the download button. If this button is clicked, we generate a unique key from the current time and store this key into our hidden field.

Then we show the loading animation by adding the CSS class "loading" to the HTML body (this is out of scope of this article as there are 100 ways to show a loading animation).

Next we start a timer which fires an event every 100ms. In that event we read all cookies of the current URL. "document.cookie" returns a string with all cookie names and values, separated with a semicolon character. We check if this string contains a cookie with the name "dlc" and a values that matches the unique key that we stored in our hidden field before.

If we have found such a cookie, the loading animation is hidden again.

Now let's look at the server side code. This is the Download method of our HomeController class:

C#
[HttpPost]
public FileResult Download(string cookieValue)
{
    // wait 10 seconds to simulate a long running operation
    System.Threading.Thread.Sleep(10000);

    // create a dummy text file to download
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("This is a demo file that has been generated on the server.");
    sb.AppendLine();
    for (int i = 1; i <= 1000; i++)
    {
        sb.AppendLine("Line " + i.ToString() + "  " + DateTime.Now.ToString());
    }

    // add a cookie with the name 'dlc' and the value from the postback
    ControllerContext.HttpContext.Response.Cookies.Add(new HttpCookie("dlc", cookieValue));

    return File(Encoding.ASCII.GetBytes(sb.ToString()), "text/plain", "data.txt");
}

The value of the hidden field is automatically passed in the cookieValue parameter of the method.

To simulate a time consuming file generation I use a Thread.Sleep to add a delay of 10 seconds. Also I generate a dummy text-file that contains 1'000 lines of text.

The most important part is adding a cookie to the response. I used "dlc" as the name of the cookie, but any name will be ok as long as you use the same name in the JavaScript code. The cookie value will be set to the cookieValue parameter.

After setting the cookie, the dummy file is returned as a FileResult.

Points of Interest

It is important to use another unique value for the cookie each time we start a download. Otherwise it would not work if someone starts the same download again because the cookie is already existing!

Although my example is using ASP.NET MVC, the principle can easily be transferred to any other programming language.

History

  • 2016-04-23 - first release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here