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



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

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

它非常适用于填充控件的所有查询——.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仍然存在。。。即使回邮已经完成,它也不会消失。

我做错了什么?

正如VDWWD所解释的("UI永远不会更新,因为服务器一次只能发送一种类型的响应(文件或html页面(">(,您真正的问题是不知道文件下载何时完成(确切地说,是准备文件的服务器端代码何时完成执行(。

SO上有很多关于这方面的问题,令人惊讶的是,答案总是你不知道

不完全是这样!

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

因此,事不宜迟,下面是代码(我只使用了loadingCss类来简化和缩小示例(:

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

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

您正在下载文件(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);
}

最新更新