【问题标题】:ASP.NET Core busy indicator while downloading file下载文件时 ASP.NET Core 繁忙指示器
【发布时间】:2020-06-11 02:56:29
【问题描述】:

在我的网页上,当用户单击下载按钮时,控制器会调用导出操作,生成一个 Excel 文件,然后将其返回给用户。根据传递给操作的参数,有时需要一些时间来生成文件并将其返回给用户。

所以,我想在等待文件时显示一个忙碌指示符。我选择了 Bootstrap 微调器。

$('.content').on('click', 'a.export', function (e) {
    e.preventDefault();

    // show busy indicator
    $('#divSpinner').css('display', 'inline-block');

    var idSelect = '';
    $('#ddlSelect option:selected').each(function () {
        idSelect += $('#ddlSelect')[0].value;
    });

    var idSelect2 = '';
    $('#ddlSelect2 option:selected').each(function () {
        idSelect2 += $('#ddlSelect2')[0].value;
    });

    var url = 'Home/Exports?' + 'param1=' + idSelect + '&param2=' + idSelect2;

    //download file
    window.location = url;

    // hide busy indicator
    $('#divSpinner').css('display', 'none');
});

好吧,这种方法有效,但问题是 $('#divSpinner').css('display', 'none'); 立即被击中,并且微调器隐藏在文件之前实际可供用户下载(浏览器下载/打开窗口)。

如何重写代码以等待 window.location=url 完成然后隐藏微调器?

编辑: 我还尝试将下载部分包装在这样的异步函数中:

async function Download(url) {

    let promise = new Promise((resolve, reject) => {
        resolve(window.location = url);
    });

        let result = await promise; // wait until the promise resolves (*)
}

并这样称呼它:

Download(url)
    .then(function() {
        $('#divSpinner').hide()
});

下载有效,但在文件下载呈现给用户之前,$('#divSpinner').hide() 仍然被点击。 async/await 的全部意义不在于在继续执行代码之前等待结果吗??

EDIT2(带有“blob”):

async function Download(url) {
    let promise = new Promise((resolve, reject) => {
        resolve(filedownload(url));
    });
    let result = await promise; 
}

function filedownload(urlToSend) {
    var req = new XMLHttpRequest();
    req.open("GET", urlToSend, true);
    req.responseType = "blob";
    req.onload = function (event) {
        var blob = req.response;
        var fileName = req.getResponseHeader("fileName") 
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = fileName;
        link.click();
    };
    req.send();
}

并这样称呼它:

Download(url)
    .then(function() {
        $('#divSpinner').hide()
});

编辑 3:我从下面使用了 @Nenad 的解决方案,但我只更改了响应标头的处理(否则我在下载时得到 NULL 文件名 - 我使用了这个解决方案 https://stackoverflow.com/a/40940790/7975627) :

function filedownload(urlToSend, resolve) {
    var req = new XMLHttpRequest();
    req.open("GET", urlToSend, true);
    req.responseType = "blob";
    req.onload = function (event) {
        var blob = req.response;
        var filename = "";
        var disposition = req.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;    
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) {
                filename = matches[1].replace(/['"]/g, '');
            }
        }
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = filename;
        link.click();
        resolve(filename); // call resolve after download has finished.
    };
    req.send();
}

【问题讨论】:

  • 这个函数是从同一个页面调用的吗?从 /Home/Exports ?
  • 顺便说一下,你的 不起作用。因为您正在更改它之前的页面。所以我想你需要让你的指标在启动时可见。当excel数据出现时,您将隐藏指标。
  • 是的,View 和 Controller 都是“Home”(Action Exports 也在 Home 控制器中)。 javascript/jquery 也是主视图的“Index.cshtml”的一部分
  • @cbalakus:。问题是它很快就被隐藏了(实际上是立即隐藏,因为代码不会在“window.location”处停止而是继续到最后,在文件准备好后提示用户保存/oen)

标签: javascript jquery async-await


【解决方案1】:
var url = 'Home/Exports?' + 'param1=' + idSelect + '&param2=' + idSelect2;

$(document).load(url, function () {
    // hide busy indicator
    $('#divSpinner').css('display', 'none');
});

我不确定,但是你能试试这个吗?

【讨论】:

  • 这种方式文件下载不起作用...微调器显示几秒钟然后消失,但不向用户提供文件下载
【解决方案2】:

TL;DR

您将控制权委派给浏览器,因此在您的页面上没有 JavaScript 通知

详细说明:

您用来下载文件的方法 (window.location = url;) 告诉浏览器加载新页面而不是当前页面。您正在将新页面的加载控制委托给浏览器,就像 url 指向另一个 HTML 页面一样。

只有在浏览器发现内容不是 HTML 而是其他内容之后,它才会根据标题下载文件,而不是抛出当前页面并显示新页面。

引导浏览器下载内容的标头是:

  • content-disposition: attachment
  • content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

不同的方法是使用 AJAX 请求下载responseType=blob。 很好的例子是here。这种方法的缺点是:

  • 旧版浏览器不支持此功能。
  • 在保存之前,整个文件已加载到 JavaScript 内存中。

但是您可以控制blob 下载后发生的事情 已完成。

更新:查看您的第二次编辑,您提前致电resolve。另外,您应该从下载方法中返回您的Promise

async function Download(url) {
    return new Promise((resolve, reject) => {
        filedownload(url, resolve); // pass resolve
    }); 
}

function filedownload(urlToSend, resolve) {
    var req = new XMLHttpRequest();
    req.open("GET", urlToSend, true);
    req.responseType = "blob";
    req.onload = function (event) {
        var blob = req.response;
        var fileName = req.getResponseHeader("fileName") 
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = fileName;
        link.click();
        resolve(fileName); // call resolve after download has finished.
    };
    req.send();
}

【讨论】:

  • 我尝试使用您链接中的 ajax 函数并将其放入我的“resolve(...)”中,但仍然存在 2 个问题:1) $('#divSpinner').css('display', 'none'); 仍然会立即被击中,2)我的下载文件名是 NULL(从控制器我返回的文件是这样的:return File(file.Contents, file.ContentType, file.Title);
  • ASP.NET MVC 部分与浏览器中的行为无关,除非您做的事情完全错误。这是 JavaScript 问题。使用 Blob 发布您的解决方案作为对问题的新编辑。
  • 你好。还有其他想法吗?
  • 你打电话给resolve 太早了。它应该在filedownload 函数中的link.click 之后调用。使用filedownload(urlToSend, resolve) 并在link.click 之后调用resolve
  • 谢谢!解决了避免 NULL 文件名的小修改(参见我上面的编辑 3)
猜你喜欢
  • 2012-05-28
  • 2012-04-10
  • 1970-01-01
  • 1970-01-01
  • 2012-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-31
相关资源
最近更新 更多