Detect when browser receives file download

前端 未结 22 1678
陌清茗
陌清茗 2020-11-21 04:55

I have a page that allows the user to download a dynamically-generated file. It takes a long time to generate, so I\'d like to show a \"waiting\" indicator. The problem is,

22条回答
  •  南方客
    南方客 (楼主)
    2020-11-21 05:46

    The question is to have a ‘waiting’ indicator while a file is generated and then return to normal once the file is downloading. The way I like todo this is using a hidden iFrame and hook the frame’s onload event to let my page know when download starts. BUT onload does not fire in IE for file downloads (like with the attachment header token). Polling the server works, but I dislike the extra complexity. So here is what I do:

    • Target the hidden iFrame as usual.
    • Generate the content. Cache it with an absolute timeout in 2 minutes.
    • Send a javascript redirect back to the calling client, essentially calling the generator page a second time. NOTE: this will cause the onload event to fire in IE because it's acting like a regular page.
    • Remove the content from the cache and send it to the client.

    Disclaimer, don’t do this on a busy site, because of the caching could add up. But really, if your sites that busy the long running process will starve you of threads anyways.

    Here is what the codebehind looks like, which is all you really need.

    public partial class Download : System.Web.UI.Page
    {
        protected System.Web.UI.HtmlControls.HtmlControl Body;
    
        protected void Page_Load( object sender, EventArgs e )
        {
            byte[ ] data;
            string reportKey = Session.SessionID + "_Report";
    
            // Check is this page request to generate the content
            //    or return the content (data query string defined)
            if ( Request.QueryString[ "data" ] != null )
            {
                // Get the data and remove the cache
                data = Cache[ reportKey ] as byte[ ];
                Cache.Remove( reportKey );
    
                if ( data == null )                    
                    // send the user some information
                    Response.Write( "Javascript to tell user there was a problem." );                    
                else
                {
                    Response.CacheControl = "no-cache";
                    Response.AppendHeader( "Pragma", "no-cache" );
                    Response.Buffer = true;
    
                    Response.AppendHeader( "content-disposition", "attachment; filename=Report.pdf" );
                    Response.AppendHeader( "content-size", data.Length.ToString( ) );
                    Response.BinaryWrite( data );
                }
                Response.End();                
            }
            else
            {
                // Generate the data here. I am loading a file just for an example
                using ( System.IO.FileStream stream = new System.IO.FileStream( @"C:\1.pdf", System.IO.FileMode.Open ) )
                    using ( System.IO.BinaryReader reader = new System.IO.BinaryReader( stream ) )
                    {
                        data = new byte[ reader.BaseStream.Length ];
                        reader.Read( data, 0, data.Length );
                    }
    
                // Store the content for retrieval              
                Cache.Insert( reportKey, data, null, DateTime.Now.AddMinutes( 5 ), TimeSpan.Zero );
    
                // This is the key bit that tells the frame to reload this page 
                //   and start downloading the content. NOTE: Url has a query string 
                //   value, so that the content isn't generated again.
                Body.Attributes.Add("onload", "window.location = 'binary.aspx?data=t'");
            }
        }
    

提交回复
热议问题