简体   繁体   English

如何让 ASP.NET 页面知道文件何时可以从服务器下载

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

I'm maintaining a legacy VB.Net Webforms App and I've got a strange issue after adding one section.我正在维护一个旧的 VB.Net Webforms 应用程序,添加一个部分后我遇到了一个奇怪的问题。

This is this code in the aspx page which shows the giphy.gif while doing a postback:这是 aspx 页面中的这段代码,它在回发时显示 giphy.gif:

<style type="text/css">
    .modalWait
    {
        position: fixed;
        top: 0;
        left: 0;
        background-color: black;
        z-index: 99;
        opacity: 0.5;
        filter: alpha(opacity=80);
        -moz-opacity: 0.8;
        min-height: 100%;
        width: 100%;
    }
    .loading
    {
        font-family: Arial;
        font-size: 10pt;
        /*border: 5px solid #67CFF5;*/
        width: 100px;
        height: 100px;
        display: none;
        position: fixed;
        background-color: transparent;
        z-index: 999;
    }
</style>

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
    function ShowProgress() {
        setTimeout(function () {
            var modal = $('<div />');
            modal.addClass("modalWait");
            $('body').append(modal);
            var loading = $(".loading");
            loading.show();
            var top = Math.max($(window).height() / 2 - loading[0].offsetHeight / 2, 0);
            var left = Math.max($(window).width() / 2 - loading[0].offsetWidth / 2, 0);
            loading.css({ top: top, left: left });
        }, 200);
    }
    $('form').live("submit", function () {
        ShowProgress();
    });
</script>

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<form runat="server">
    <asp:ScriptManager runat="server"></asp:ScriptManager>

        <!-- Page Wrapper -->
        <div id="wrapper">

        <!-- More Code -->

        </div>
        <!-- End of Page Wrapper -->

        <div class="loading" align="center">
            <%--Loading. Please wait.<br /><br />--%>
            <img src="../img/giphy.gif" />
        </div>

</form>

And works perfectly for all the queries which populate the controls - the.gif appears while the database is called and then goes away.并且对于填充控件的所有查询都非常有效 - the.gif 在调用数据库时出现然后消失。

But then I added a function to read the datagrid into a.csv and then download it.但后来我添加了一个 function 来将数据网格读入 a.csv 然后下载。 It works perfectly, except when I add this section in the code behind:它工作得很好,除非我在后面的代码中添加这部分:

Dim bytes As Byte() = Encoding.ASCII.GetBytes(sb.ToString())

Response.Clear()
Response.ContentType = "text/csv"
Response.AddHeader("Content-Length", bytes.Length.ToString())
Response.AddHeader("Content-disposition", "attachment; filename=contacts.csv")
Response.Write(sb.ToString())
Response.Flush()
Response.End()

The file downloads perfectly... but the giphy.gif is still there... it doesn't go away even though the postback has finished.该文件下载完美......但 giphy.gif 仍然存在......即使回发完成,它也不会 go 消失。

What am I doing wrong?我究竟做错了什么?

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).正如 VDWWD 解释的那样( “UI 永远不会更新,因为服务器一次只能发送一种类型的响应(文件或 html 页面)” )您真正的问题是您不知道文件下载何时完成(或确切地说,当准备文件的服务器端代码完成执行时)。

There are more than a handful of questions about this here on SO and amazingly almost always the answer is you cannot know ! SO上有很多关于这个的问题,令人惊讶的是,答案几乎总是你不知道

Well, not quite!好吧,不完全是!

You can always let the client know by sending back a uniquely named cookie containing a time stamp.您始终可以通过发回包含时间戳的唯一命名 cookie 来让客户端知道。 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.在客户端上,javascript 代码尝试检测 cookie 的存在,当它检测到时,它会隐藏您显示的任何加载图像(或文本或其他内容)。

So, without further ado here's the code (I only used the loading css class to make the sample simpler and smaller):所以,不用多说这里的代码(我只使用loading css class 使示例更简单和更小):

<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 :代码隐藏 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# :代码隐藏 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.如果是这样,我建议我们编辑问题的标题(可能还有一些内容),以帮助其他人最终找到一个有效的解决方案来解决一直没有真正得到解答的问题。

You are using ScriptManager without UpdatePanel Try to replace (Where start <ScriptManager ……..) with this:您正在使用没有 UpdatePanel 的 ScriptManager 尝试将 (Where start <ScriptManager ........) 替换为:

<asp:ScriptManager ID="ScriptManager1" runat="server"/> 
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <ContentTemplate>


    <!-- Page Wrapper -->
    <div id="wrapper">

        <!-- More Code -->

    </div>
    <!-- End of Page Wrapper -->


    <div class="loading" style="text-align:center">
        <%--Loading. Please wait.<br /><br />--%>
        <img src="../img/giphy.gif" />
    </div>



    </ContentTemplate>
</asp:UpdatePanel>

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.您正在下载一个文件( contacts.csv ),UI 永远不会更新,因为服务器一次只能发送一种类型的响应(文件或 html 页面),因此下载后图像将保持可见。

To fix this either open the download in a new browser window.要解决此问题,请在新浏览器 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.如果下载必须在 PostBack 之后进行,则上述解决方案将不起作用。 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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM