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

Posted

技术标签:

【中文标题】如何让 ASP.NET 页面知道文件何时可以从服务器下载【英文标题】:How to let a ASP.NET page know when a file is ready for download from the server 【发布时间】:2020-03-15 20:28:09 【问题描述】:

我正在维护一个旧版 VB.Net Webforms 应用程序,添加一个部分后我遇到了一个奇怪的问题。

这是在进行回发时显示 giphy.gif 的 aspx 页面中的代码:

<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>

并且非常适合填充控件的所有查询 - .gif 在调用数据库时出现然后消失。

但后来我添加了一个函数来将数据网格读入 .csv 然后下载它。它工作得很好,除非我在后面的代码中添加这部分:

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()

文件下载完美……但 giphy.gif 仍然存在……即使回发完成,它也不会消失。

我做错了什么?

【问题讨论】:

我在一个应用程序中遇到了类似的问题,但不同之处在于它会引发内部异常并仍然下载文件,就我而言,我建议将该 VB 代码放入 ASHX 形式并将客户端重定向到该表单以下载文件 【参考方案1】:

正如 VDWWD 解释的那样(“UI 永远不会更新,因为服务器一次只能发送一种类型的响应(文件或 html 页面)”)你真正的问题是你有不知道什么时候文件下载完成(或者准确地说,是准备文件的服务器端代码完成执行的时候)。

关于这个here on SO 的问题不止少数,而且令人惊讶的是,答案几乎总是你不知道

嗯,不完全是!

您始终可以通过发回包含时间戳的唯一命名 cookie 来让客户端知道。在客户端,javascript 代码会尝试检测 cookie 的存在,当它检测到时,它会隐藏您显示的任何加载图像(或文本或其他内容)。

所以,事不宜迟,这里是代码(我只使用了loading css 类来使示例更简单和更小):

<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>

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

代码隐藏 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();

我希望这会有所帮助。如果是这样,我建议我们编辑问题的标题(可能还有一些内容),以帮助其他人最终找到一个有效的解决方案来解决一直没有真正得到解答的问题。

【讨论】:

太棒了@dpant ...!太感谢了。是的,如果你认为应该编辑标题,我很高兴。【参考方案2】:

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

要解决此问题,请在新的浏览器窗口中打开下载。

<a href="/DownloadFile.aspx" target="_blank">Download CSV</a>

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

如果下载必须在 PostBack 之后进行,则上述解决方案将不起作用。一个可能的解决方案是在文件的平均下载时间之后隐藏图像。

function ShowProgress() 

    //rest of code

    setTimeout(function () 
        loading.hide();
    , 2000);

【讨论】:

这不是通过 ashx 调用而是通过 aspx 和 .vb 文件完成的。如何让 Response.Write(sb.ToString()) 打开一个新标签? 你不能。或者您需要创建一个到特定端点的 ajax 帖子。然后您可以在单独的线程中下载文件并显示/隐藏图像。

以上是关于如何让 ASP.NET 页面知道文件何时可以从服务器下载的主要内容,如果未能解决你的问题,请参考以下文章

检测何时在 ASP.NET 中禁用 JavaScript

从 ASP.NET 服务器控件动态添加 CSS 文件

ASP.NET Application_End()这个事件何时执行?

ASP.NET 用VS2005创建的网站如何让其他人浏览

如何从php调用“asp.net api上传文件”

将标签字段添加到 asp.net 网页