等待下载开始时显示动画
这篇文章展示了一个很酷的技巧,用于在等待下载开始时显示加载动画。如果下载开始,动画会自动隐藏。
引言
在 Web 应用程序中,您经常需要下载在服务器上动态创建的文件。如果这需要几秒钟以上的时间,您希望向用户显示某种反馈,例如“请稍候...”消息和一个漂亮的加载动画
当单击下载链接或按钮并将请求发送回服务器时,使用 JavaScript 显示这样的动画很容易。但是,当服务器直接用要下载的文件进行响应时,无法钩住该事件并隐藏动画。
我将向您展示一个巧妙的技巧,以便在下载准备就绪并且浏览器显示下载对话框时隐藏动画。
背景
我使用的技巧利用了一个cookie:在服务器开始将生成的文件写入响应流之前,它会设置一个cookie。该cookie作为响应的HTTP标头的一部分发送到浏览器,并在浏览器接收到响应的第一个字节后立即可用。
cookie 也可以从 JavaScript 读取。所以我们所做的是启动一个计时器,该计时器轮询此cookie是否存在,当按下下载链接/按钮时。此计时器在请求运行时继续工作。一旦我们找到了我们的cookie,我们就知道下载已准备就绪,并且可以隐藏动画。
使用代码
让我们首先看一下客户端代码。这是 MVC 视图
@using (Html.BeginForm("Download", "Home"))
{
@Html.Hidden("cookieValue")
<input type="submit" id="btnDownload" value="Download!" />
}
我们有一个表单,它将发出一个 HTTP POST 并调用名为“Home”的控制器中的一个名为“Download”的动作。在表单中,我们有一个隐藏字段和一个启动下载的按钮。
<script>
var _tmr;
$(function () {
$('#btnDownload').click(function () {
// save timestamp in hidden field
$('#cookieValue').val(Date.now().toString());
// show animation
$('body').addClass("loading");
// start timer to wait for cookie
_tmr = window.setInterval(function () {
var _str = 'dlc=' + $('#cookieValue').val();
if (document.cookie.indexOf(_str) !== -1) {
// hide animation
$('body').removeClass("loading");
}
}, 100);
});
});
</script>
使用 jQuery,我们为下载按钮附加一个点击事件。如果单击此按钮,我们将从当前时间生成一个唯一的键,并将此键存储到我们的隐藏字段中。
然后,我们通过将 CSS 类“loading”添加到 HTML 主体来显示加载动画(这超出了本文的范围,因为有 100 种显示加载动画的方法)。
接下来,我们启动一个计时器,该计时器每 100 毫秒触发一个事件。在该事件中,我们读取当前 URL 的所有 cookie。“document.cookie”返回一个包含所有 cookie 名称和值的字符串,用分号分隔。我们检查此字符串是否包含一个名为“dlc”的 cookie,其值与我们之前存储在隐藏字段中的唯一键匹配。
如果我们找到了这样的cookie,加载动画将再次隐藏。
现在让我们看看服务器端代码。这是我们 HomeController
类的 Download
方法
[HttpPost]
public FileResult Download(string cookieValue)
{
// wait 10 seconds to simulate a long running operation
System.Threading.Thread.Sleep(10000);
// create a dummy text file to download
StringBuilder sb = new StringBuilder();
sb.AppendLine("This is a demo file that has been generated on the server.");
sb.AppendLine();
for (int i = 1; i <= 1000; i++)
{
sb.AppendLine("Line " + i.ToString() + " " + DateTime.Now.ToString());
}
// add a cookie with the name 'dlc' and the value from the postback
ControllerContext.HttpContext.Response.Cookies.Add(new HttpCookie("dlc", cookieValue));
return File(Encoding.ASCII.GetBytes(sb.ToString()), "text/plain", "data.txt");
}
隐藏字段的值将自动传递到该方法的 cookieValue
参数中。
为了模拟耗时的文件生成,我使用 Thread.Sleep
来增加 10 秒的延迟。我还生成一个包含 1'000 行文本的虚拟文本文件。
最重要的一部分是向响应添加一个 cookie。我使用“dlc”作为 cookie 的名称,但任何名称都可以,只要您在 JavaScript 代码中使用相同的名称即可。 cookie 值将设置为 cookieValue
参数。
设置 cookie 后,虚拟文件将作为 FileResult
返回。
关注点
每次我们开始下载时,使用另一个唯一的值作为 cookie 非常重要。否则,如果有人再次启动相同的下载,它将无法工作,因为 cookie 已经存在!
尽管我的示例使用 ASP.NET MVC,但该原则可以很容易地转移到任何其他编程语言。
历史
- 2016-04-23 - 初次发布