How to let a ASP.NET page know when a file is ready for download from the server

后端 未结 2 1972
南旧
南旧 2021-01-07 11:01

I\'m maintaining a legacy VB.Net Webforms App and I\'ve got a strange issue after adding one section.

This is this code in the aspx page which shows the giphy.gif w

相关标签:
2条回答
  • 2021-01-07 11:38

    As VDWWD explained ("the UI is never updated because the server can only send one type of response at a time (a file or a html page)") your real problem is that you have no idea when the file download gets completed (or to be exact, when the server-side code, which prepares the file, has finished executing).

    There are more than a handful of questions about this here on SO and amazingly almost always the answer is you cannot know!

    Well, not quite!

    You can always let the client know by sending back a uniquely named cookie containing a time stamp. On the client, the javascript code tries to detect the cookie presence and when it does it hides any loading image (or text or whatever) you've shown.

    So, without further ado here's the code (I only used the loading css class to make the sample simpler and smaller):

    <form id="form1" runat="server">        
        <div class="loading">
            Loading. Please wait.<br />
            <br />
        </div>
        <asp:HiddenField ID="windowid" runat="server" />        
        <asp:Button ID="Button1" runat="server" Text="Download" 
            OnClick="Button1_Click" 
            OnClientClick="ShowProgress();" />
    </form>
    
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>  
    
    <script type="text/javascript">
        function ShowProgress() {           
            $(".loading").show();
            checkCookie();
        }
    
        function checkCookie() {
            var cookieVal = $.cookie($('#<%= windowid.ClientID %>').val());
            if (cookieVal == null || cookieVal === 'undefined') {
                setTimeout("checkCookie();", 1000);
            }
            else {
                $(".loading").hide();
            }
        }  
    </script>
    

    Code-behind VB.NET:

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not IsPostBack Then
            ' the hidden value will be used to uniquely name the cookie and
            ' will be used both on the server and the client side to
            ' work with the cookie.
            windowid.Value = Guid.NewGuid().ToString()
        End If
    End Sub
    
    Protected Sub Button1_Click(sender As Object, e As EventArgs)
        ' for demo purposes only
        System.Threading.Thread.Sleep(4000)
    
        GetCsv()
    End Sub
    
    Protected Sub GetCsv()
        ' ... 
    
        Dim bytes As Byte() = Encoding.ASCII.GetBytes(sb.ToString())
    
        Response.Clear()
        Response.Cookies.Add(New HttpCookie(windowid.Value, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss:ff")))
        Response.AddHeader("Content-Disposition", "attachment; filename=contacts.csv")
        Response.AddHeader("Content-Length", bytes.Length.ToString())
        Response.ContentType = "text/csv"
        Response.Write(sb.ToString())
        Response.Flush()
        Response.End()
    End Sub
    

    Code-behind C#:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            // the hidden value will be used to uniquely name the cookie and
            // will be used both on the server and the client side to
            // work with the cookie.
            windowid.Value = Guid.NewGuid().ToString();
        }
    }
    
    
    public void GetCsv()
    {
        // ...    
        var bytes = Encoding.ASCII.GetBytes(sb.ToString());
    
        Response.Clear();
        Response.Cookies.Add(new HttpCookie(windowid.Value, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss:ff")));
        Response.AddHeader("Content-Disposition", "attachment; filename=contacts.csv");
        Response.AddHeader("Content-Length", bytes.Length.ToString());
        Response.ContentType = "text/csv";
        Response.Write(sb.ToString());
        Response.Flush();
        Response.End();
    }
    
    protected void Button1_Click(object sender, EventArgs e)
    {
        // for demo purposes only
        System.Threading.Thread.Sleep(2000);    
    
        GetCsv();
    }
    

    I hope this helps. If it does I suggest we edit the title (and maybe some of the content) of the question so as to help others finally find a working solution to a problem that hasn't been really answered all this time.

    0 讨论(0)
  • 2021-01-07 11:40

    You are downloading a file (contacts.csv) the UI is never updated because the server can only send one type of response at a time (a file or a html page), so the image will remain visible after download.

    To fix this either open the download in a new browser window.

    <a href="/DownloadFile.aspx" target="_blank">Download CSV</a>
    
    <a href="/handler1.ashx?file=45" target="_blank">Download CSV</a>
    

    If the download must come after a PostBack then the above solution will not work. A possible solution would be to just hide the image after the average download time of the file.

    function ShowProgress() {
    
        //rest of code
    
        setTimeout(function () {
            loading.hide();
        }, 2000);
    }
    
    0 讨论(0)
提交回复
热议问题