Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute javascript code at the end of event handler

I'm building a web application on ASP.NET with C#. On a button click I show a loading image, while the database query is being executed. Then I dynamically create an Excel file and send it to the client like this:

HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-disposition", "attachment;filename=" + filename + ".xlsx");
HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.Unicode;
HttpContext.Current.Response.BinaryWrite(p.GetAsByteArray());
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.End(); 

I get the dialog box, and the loading image stays there.

I've tried placing call to a javascript function (with ClientScript.RegisterStartupScript function) before the code above, it didn't work. As I understand all javascript code is run after all the code-behind has executed, but in this case it doesn't execute at all once the file is sent to the client.

I've also tried creating a separate thread, and removing the loading image there. I put the breakpoint to trace it, code in the thread does execute, but the image still stays there.

Does anyone have an idea how this can be handled? Thank you!

like image 477
gg17 Avatar asked Oct 02 '22 16:10

gg17


1 Answers

You can only send or transmit 1 mime type in one request/response cycle. (My knowledge in this area is debatable).

That said, you can design a hack around this. Use an iframe on the client to "download the file". You can point its src to an ashx file that does the same.

You need to wire the iframe's onload event, so your web page has someway of knowing that download is done; thats where you can execute your logic.

Solution Update:

Well, after digging around, I've discovered my answer is half-baked!

The issue is that iframes don't trigger their onload event after they download something. The onload event will trigger iff there the url pointed to by src actually navigates to a different page. This is by design I suppose. And I learn that today!

So what then is the work-around?!

Fortunately, you can transmit cookies to the client. On the client your web page has to keep polling for the presence of this cookie. So once your web page is able to detect the presence of the cookie, it means that the browser has completed with the download request. This has been discussed in great detail in the following post:

http://geekswithblogs.net/GruffCode/archive/2010/10/28/detecting-the-file-download-dialog-in-the-browser.aspx

I'll just show you some code relating to the handler file (which simulates a download), and the client (which has an iframe doing the job). This should pretty much give you the gist:

Webform1.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApp.FileDownload.WebForm1" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>iFrame Download</title>
    <script type="text/javascript" src="Scripts/jquery-2.1.0.min.js"></script>
    <script type="text/javascript" src="Scripts/jquery.cookie.js"></script>
    <script type="text/javascript">
        function foo() {
            console.log('foo');
            //execute post-download logic here
        }
        $(function () {            
            $('input').click(function () {
                //make sure we get rid of the 
                //cookie before download
                $.removeCookie('downloaded');

                var intrvl = setTimeout(function () { //this function polls for the cookie through which we track that the file has indeed been downloaded
                    console.log('timer');
                    var value = $.cookie('downloaded');
                    if (value == 'true') {
                        clearTimeout(intrvl);
                        foo();
                    }
                }, 1000);

                //this initiates the download
                $('iframe').attr({
                    'src': 'download.ashx?id=' + $('#tbxRandomNumber').val()
                });

            });
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:TextBox ID="tbxRandomNumber" runat="server"></asp:TextBox>
        <input type="button" value="Download" />
        <iframe src="about:blank" style="display:none"></iframe>
        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Next Random Number" />
    </div>
    </form>
</body>
</html>

I've made used of jquery cookies plugin to help me with handling cookies.

download.ashx:

using System;
using System.Web;

namespace WebApp.FileDownload
{
    /// <summary>
    /// Summary description for download
    /// </summary>
    public class download : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {            
            context.Response.ContentType = "text/plain";
            context.Response.SetCookie(new HttpCookie("downloaded","true")); //setting cookie in the response
            string id = context.Request.QueryString["id"] == null ? "NULL" : context.Request.QueryString["id"];
            string str = string.Format("Content with id {0} was generated at {1}", id, DateTime.Now.ToLongTimeString());

            context.Response.AddHeader("Content-Disposition", "attachment; filename=test.txt");
            context.Response.AddHeader("Content-Length", str.Length.ToString());
            context.Response.Write(str);
            context.Response.End();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}
like image 146
deostroll Avatar answered Oct 13 '22 11:10

deostroll