ChatGPT解决这个技术问题 Extra ChatGPT

处理从 ajax post 下载的文件

我有一个将 ajax POST 请求发送到某个 URL 的 javascript 应用程序。响应可能是 JSON 字符串,也可能是文件(作为附件)。我可以在我的 ajax 调用中轻松检测 Content-Type 和 Content-Disposition,但是一旦我检测到响应包含一个文件,我如何让客户端下载它?我在这里阅读了许多类似的主题,但没有一个提供我正在寻找的答案。

请,请,请不要发布建议我不应该为此使用 ajax 或我应该重定向浏览器的答案,因为这都不是一个选项。使用纯 HTML 表单也不是一种选择。我需要的是向客户端显示一个下载对话框。这可以做到吗?怎么做?

对于阅读这篇文章的人,请阅读这篇文章:stackoverflow.com/questions/20830309/…
我已从问题中删除了您的解决方案。欢迎您将其发布为下面的答案帖子,但它不属于问题帖子。

J
Jonathan Amend

不要这么快放弃,因为(在现代浏览器中)可以使用部分 FileAPI 来完成:

var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'blob';
xhr.onload = function () {
    if (this.status === 200) {
        var blob = this.response;
        var filename = "";
        var disposition = xhr.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, '');
        }

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') {
                    window.location.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send($.param(params, true));

或者如果使用 jQuery.ajax:

$.ajax({
    type: "POST",
    url: url,
    data: params,
    xhrFields: {
        responseType: 'blob' // to avoid binary data being mangled on charset conversion
    },
    success: function(blob, status, xhr) {
        // check for a filename
        var filename = "";
        var disposition = xhr.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, '');
        }

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') {
                    window.location.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
});

您的代码已经为我工作了很长一段时间,但最近停止了。问题是 Chrome、Edge 等中的内容配置始终为空......有什么原因以及如何规避这个问题?它是根据两个浏览器中的开发人员工具栏返回的,但是在检查 xhr.getResponseHeader('Content-Disposition') 时,它始终为空。
@Thierry 可能是 CORS 问题;检查 Access-Control-Expose-Headers 是否包含 Content-Disposition(或通配符)。
谢谢你回来。我也发现了这一点,但我不得不引入 withCredentials xhrFields,而不是将其添加到现有的条目中,我创建了一个单独的条目,导致“blob”的条目被覆盖! 🤦🤦。通过将两者合并到相同的 xhrFields 下解决了问题。我的愚蠢错误!再次感谢。
佚名

创建一个表单,使用 POST 方法,提交表单 - 不需要 iframe。当服务器页面响应请求时,为文件的 mime 类型编写响应标头,它将显示一个下载对话框 - 我已经这样做了很多次。

您想要应用程序/下载的内容类型 - 只需搜索如何为您使用的任何语言提供下载。


如问题所述:“使用纯 HTML 表单也不是一种选择。”
不,因为使用常规 POST 会将浏览器导航到 POST URL。我不想离开页面。我想在后台执行请求,处理响应并将其呈现给客户端。
如果服务器像其他答案一样发回标头,它会在新窗口中打开 - 我以前做过。只有当您的服务器端脚本返回 HTML 代码时,它才会离开
@PavlePredic 您是否最终弄清楚如何管理这两种响应场景,即 JSON 文本响应或下载文件响应?
答案不清楚,建议的解决方案不起作用。
N
Naren Yellavula

我遇到了同样的问题并成功解决了。我的用例是这样的。

“将 JSON 数据发布到服务器并接收一个 excel 文件。该 excel 文件由服务器创建并作为对客户端的响应返回。将该响应作为具有自定义名称的文件下载到浏览器中”

$("#my-button").on("click", function(){

// Data to post
data = {
    ids: [1, 2, 3, 4, 5]
};

// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    var a;
    if (xhttp.readyState === 4 && xhttp.status === 200) {
        // Trick for making downloadable link
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        // Give filename you wish to download
        a.download = "test-file.xls";
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
    }
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});

上面的代码片段只是在做以下事情

使用 XMLHttpRequest 将数组作为 JSON 发布到服务器。

在以 blob(二进制)形式获取内容后,我们正在创建一个可下载的 URL 并将其附加到不可见的“a”链接上,然后单击它。

这里我们需要在服务器端仔细设置一些东西。我在 Python Django HttpResponse 中设置了一些标头。如果您使用其他编程语言,则需要相应地设置它们。

# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

由于我在这里下载 xls(excel),所以我将 contentType 调整为上述一个。您需要根据您的文件类型进行设置。您可以使用此技术下载任何类型的文件。


j
jwfearn

您使用的是什么服务器端语言?在我的应用程序中,我可以通过在 PHP 的响应中设置正确的标头轻松地从 AJAX 调用下载文件:

设置标头服务器端

header("HTTP/1.1 200 OK");
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

// The optional second 'replace' parameter indicates whether the header
// should replace a previous similar header, or add a second header of
// the same type. By default it will replace, but if you pass in FALSE
// as the second argument you can force multiple headers of the same type.
header("Cache-Control: private", false);

header("Content-type: " . $mimeType);

// $strFileName is, of course, the filename of the file being downloaded. 
// This won't have to be the same name as the actual file.
header("Content-Disposition: attachment; filename=\"{$strFileName}\""); 

header("Content-Transfer-Encoding: binary");
header("Content-Length: " . mb_strlen($strFile));

// $strFile is a binary representation of the file that is being downloaded.
echo $strFile;

这实际上会将浏览器“重定向”到这个下载页面,但正如@ahren 在他的评论中所说,它不会离开当前页面。

一切都是为了设置正确的标头,所以如果不是 PHP,我相信您会为您正在使用的服务器端语言找到合适的解决方案。

处理响应客户端

假设您已经知道如何进行 AJAX 调用,那么在客户端您向服务器执行 AJAX 请求。然后,服务器会生成一个链接,从该链接可以下载此文件,例如您要指向的“转发”URL。例如,服务器响应:

{
    status: 1, // ok
    // unique one-time download token, not required of course
    message: 'http://yourwebsite.com/getdownload/ska08912dsa'
}

处理响应时,您在正文中注入一个 iframe,并将 iframe 的 SRC 设置为您刚刚收到的 URL,如下所示(使用 jQuery 来简化本示例):

$("body").append("<iframe src='" + data.message +
  "' style='display: none;' ></iframe>");

如果您设置了正确的标题,如上所示,iframe 将强制一个下载对话框,而不会将浏览器导航离开当前页面。

笔记

与您的问题有关的额外补充;我认为使用 AJAX 技术请求内容时最好总是返回 JSON。收到 JSON 响应后,您可以决定客户端如何处理它。例如,也许稍后您希望用户单击 URL 的下载链接而不是直接强制下载,在您当前的设置中,您必须同时更新客户端和服务器端才能这样做。


C
Community

这是我如何使这项工作https://stackoverflow.com/a/27563953/2845977

$.ajax({ url: '', 成功: function(data) { var blob=new Blob([data]); var link=document.createElement('a'); link.href=window.URL .createObjectURL(blob); link.download=""; link.click(); } });

使用 download.js 更新了答案

$.ajax({ url: '', 成功: download.bind(true, "", "") });


嗨,我需要 jQuery 3.0 > 才能工作吗?
我还得到了一个空白的 pdf,里面有你给出的两个例子。我试图让它下载一个pdf文件。
@gbade_ 不,您不需要任何特定的 jQuery 版本。应该适用于所有版本。您是否检查了您下载的 PDF 是否具有正确的 CORS 标头?控制台上的任何错误都可能有助于调试
这对我使用 download.js 有用:success: function (response, status, request) { download(response, "filename.txt", "application/text"); }
T
Tim Hettler

对于那些从 Angular 角度寻找解决方案的人来说,这对我有用:

$http.post(
  'url',
  {},
  {responseType: 'arraybuffer'}
).then(function (response) {
  var headers = response.headers();
  var blob = new Blob([response.data],{type:headers['content-type']});
  var link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = "Filename";
  link.click();
});

这有帮助,但我需要保留原始文件名。我在“Content-Disposition”下的响应标头中看到了文件名,但我在代码的响应对象中找不到该值。设置 link.download = "" 会产生一个随机的 guid 文件名,而 link.download = null 会产生一个名为“null”的文件。
@Marie,您可以使用 INPUT 元素的 HTMLInputElement.files 属性在上传时记录文件名。有关详细信息,请参阅 The MDN docs on the file input
A
Alain Cruz

对于那些寻求更现代方法的人,您可以使用 fetch API。以下代码显示了如何下载电子表格文件。

fetch(url, {
    body: JSON.stringify(data),
    method: 'POST',
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    },
})
.then(response => response.blob())
.then(response => {
    const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
    const downloadUrl = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = downloadUrl;
    a.download = "file.xlsx";
    document.body.appendChild(a);
    a.click();
})

我相信这种方法比其他XMLHttpRequest解决方案更容易理解。此外,它具有与 jQuery 方法类似的语法,无需添加任何其他库。

当然,我建议检查您正在开发的浏览器,因为这种新方法不适用于 IE。您可以在以下 [链接][1] 上找到完整的浏览器兼容性列表。

重要提示:在此示例中,我将向侦听给定 url 的服务器发送 JSON 请求。此 url 必须设置,在我的示例中,我假设您知道这部分。此外,请考虑您的请求工作所需的标头。由于我发送的是 JSON,我必须添加 Content-Type 标头并将其设置为 application/json; charset=utf-8,以便让服务器知道它将接收的请求类型。


惊人的!要在新选项卡而不是下载弹出窗口中打开它:使用 ``` const window = open(downloadUrl, "_blank"); if (window !== null) window.focus(); ```
我有办法对多组数据执行此操作吗?例如,从 api 调用中取回 { fileOne: data, fileTwo: data, fileThree: data } 并一次生成三个下载的文件?谢谢!
嗯,我没试过。但是您始终可以将图像压缩为 zip 文件并下载。我会检查是否可能。
J
José SAYAGO

我看到您已经找到了解决方案,但是我只是想添加一些信息,这些信息可能有助于尝试通过大型 POST 请求实现相同目标的人。

几周前我遇到了同样的问题,确实不可能通过 AJAX 实现“干净”下载,Filament Group 创建了一个 jQuery 插件,它的工作方式与您已经发现的完全一样,它被称为 {1 } 然而,这种技术有一个缺点。

如果您通过 AJAX 发送大请求(例如文件 +1MB),则会对响应能力产生负面影响。在缓慢的 Internet 连接中,您必须等待很长时间才能发送请求并等待文件下载。它不像即时的“点击”=>“弹出”=>“下载开始”。它更像是“点击”=>“等待数据发送”=>“等待响应”=>“下载开始”,这使得文件看起来是两倍大小,因为您必须等待发送请求通过 AJAX 并将其作为可下载文件取回。

如果您使用 <1MB 的小文件,您不会注意到这一点。但正如我在自己的应用程序中发现的那样,对于更大的文件大小几乎无法忍受。

我的应用程序允许用户导出动态生成的图像,这些图像通过 POST 请求以 base64 格式发送到服务器(这是唯一可能的方式),然后处理并以 .png、.jpg 文件、base64 的形式发送回用户图像 +1MB 的字符串很大,这迫使用户等待文件开始下载所需的时间。在缓慢的互联网连接中,它可能真的很烦人。

我的解决方案是将文件临时写入服务器,一旦准备好,以按钮的形式动态生成指向文件的链接,该按钮在“请稍候...”和“下载”状态之间同时切换时间,在预览弹出窗口中打印 base64 图像,以便用户可以“右键单击”并保存它。这使得所有等待时间对用户来说更容易忍受,也加快了速度。

2014 年 9 月 30 日更新:

自从我发布这篇文章以来已经过去了几个月,我终于找到了一种更好的方法来加快处理大型 base64 字符串的速度。我现在将 base64 字符串存储到数据库中(使用 longtext 或 longblog 字段),然后我通过 jQuery File Download 传递它的记录 ID,最后在下载脚本文件上我使用此 ID 查询数据库以提取 base64 字符串并传递它下载功能。

下载脚本示例:

<?php
// Record ID
$downloadID = (int)$_POST['id'];
// Query Data (this example uses CodeIgniter)
$data       = $CI->MyQueries->GetDownload( $downloadID );
// base64 tags are replaced by [removed], so we strip them out
$base64     = base64_decode( preg_replace('#\[removed\]#', '', $data[0]->image) );
// This example is for base64 images
$imgsize    = getimagesize( $base64 );
// Set content headers
header('Content-Disposition: attachment; filename="my-file.png"');
header('Content-type: '.$imgsize['mime']);
// Force download
echo $base64;
?>

我知道这超出了 OP 的要求,但是我觉得用我的发现来更新我的答案会很好。当我在寻找我的问题的解决方案时,我阅读了很多“从 AJAX POST 数据下载”线程,这些线程并没有给我我正在寻找的答案,我希望这些信息可以帮助那些想要实现类似目标的人。


jQuery File Download 仅将我重定向到 url。我这样称呼它:jQuery.download("api/ide/download-this-file.php", {filePath: path2Down}, "POST");
L
Ludovic Martin

这是我使用临时隐藏表单的解决方案。

//Create an hidden form
var form = $('<form>', {'method': 'POST', 'action': this.href}).hide();

//Add params
var params = { ...your params... };
$.each(params, function (k, v) {
    form.append($('<input>', {'type': 'hidden', 'name': k, 'value': v}));
});

//Make it part of the document and submit
$('body').append(form);
form.submit();

//Clean up
form.remove();

请注意,我大量使用 JQuery,但您可以使用原生 JS 做同样的事情。


t
tepez

我想指出在接受的答案中使用该技术时出现的一些困难,即使用表单帖子:

您不能在请求上设置标头。如果您的身份验证架构涉及标头,即在 Authorization 标头中传递的 Json-Web-Token,则您必须找到其他方式来发送它,例如作为查询参数。您无法真正判断请求何时完成。好吧,您可以使用在响应时设置的 cookie,如 jquery.fileDownload 所做的那样,但它远非完美。它不适用于并发请求,如果响应永远不会到达,它将中断。如果服务器响应错误,用户将被重定向到错误页面。您只能使用表单支持的内容类型。这意味着您不能使用 JSON。

我最终使用了将文件保存在 S3 上并发送预签名 URL 来获取文件的方法。


K
KurtPreston

正如其他人所说,您可以创建并提交表单以通过 POST 请求下载。但是,您不必手动执行此操作。

一个非常简单的库就是 jquery.redirect。它提供了一个类似于标准 jQuery.post 方法的 API:

$.redirect(url, [values, [method, [target]]])

J
Jairo Miranda

这是一个 3 岁的问题,但我今天遇到了同样的问题。我查看了您编辑的解决方案,但我认为它会牺牲性能,因为它必须提出双重请求。因此,如果有人需要另一种不意味着两次调用服务的解决方案,那么这就是我这样做的方式:

<form id="export-csv-form" method="POST" action="/the/path/to/file">
    <input type="hidden" name="anyValueToPassTheServer" value="">
</form>

此表单仅用于调用服务并避免使用 window.location()。之后,您只需从 jquery 提交表单即可调用服务并获取文件。这很简单,但这样您就可以使用 POST 进行下载。我现在认为,如果您调用的服务是 GET,这可能会更容易,但这不是我的情况。


C
Community

我用过这个FileSaver.js。在我使用 csv 文件的情况下,我这样做了(在 coffescript 中):

  $.ajax
    url: "url-to-server"
    data: "data-to-send"
    success: (csvData)->
      blob = new Blob([csvData], { type: 'text/csv' })
      saveAs(blob, "filename.csv")

我认为对于最复杂的情况,必须正确处理数据。在引擎盖下 FileSaver.js 实现与 Jonathan Amend 答案相同的方法。


..但是您通常可以在iOS中下载文件吗?
S
Samantha Adrichem

请参阅:http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/ 它会返回一个 blob 作为响应,然后可以将其放入 filesaver


虽然这在理论上可以回答问题,但it would be preferable在此处包含答案的基本部分,并提供链接以供参考。
D
Dvs Prajapati

这是我的解决方案,从不同来源收集:服务器端实现:

    String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
    // Set headers
    response.setHeader("content-disposition", "attachment; filename =" + fileName);
    response.setContentType(contentType);
    // Copy file to output stream
    ServletOutputStream servletOutputStream = response.getOutputStream();
    try (InputStream inputStream = new FileInputStream(file)) {
        IOUtils.copy(inputStream, servletOutputStream);
    } finally {
        servletOutputStream.flush();
        Utils.closeQuitely(servletOutputStream);
        fileToDownload = null;
    }

客户端实现(使用jquery):

$.ajax({
type: 'POST',
contentType: 'application/json',
    url: <download file url>,
    data: JSON.stringify(postObject),
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert(errorThrown);
    },
    success: function(message, textStatus, response) {
       var header = response.getResponseHeader('Content-Disposition');
       var fileName = header.split("=")[1];
       var blob = new Blob([message]);
       var link = document.createElement('a');
       link.href = window.URL.createObjectURL(blob);
       link.download = fileName;
       link.click();
    }
});   

t
ticky

下面是我下载多个文件的解决方案,具体取决于包含一些 id 的列表并在数据库中查找,文件将被确定并准备好下载 - 如果存在的话。我正在使用 Ajax 为每个文件调用 C# MVC 操作。

是的,就像其他人说的那样,可以在 jQuery Ajax 中做到这一点。我用 Ajax 成功做到了,我总是发送响应 200。

所以,这是关键:

  success: function (data, textStatus, xhr) {

这是我的代码:

var i = 0;
var max = 0;
function DownloadMultipleFiles() {
            if ($(".dataTables_scrollBody>tr.selected").length > 0) {
                var list = [];
                showPreloader();
                $(".dataTables_scrollBody>tr.selected").each(function (e) {
                    var element = $(this);
                    var orderid = element.data("orderid");
                    var iscustom = element.data("iscustom");
                    var orderlineid = element.data("orderlineid");
                    var folderPath = "";
                    var fileName = "";

                    list.push({ orderId: orderid, isCustomOrderLine: iscustom, orderLineId: orderlineid, folderPath: folderPath, fileName: fileName });
                });
                i = 0;
                max = list.length;
                DownloadFile(list);
            }
        }

然后调用:

function DownloadFile(list) {
        $.ajax({
            url: '@Url.Action("OpenFile","OrderLines")',
            type: "post",
            data: list[i],
            xhrFields: {
                responseType: 'blob'
            },
            beforeSend: function (xhr) {
                xhr.setRequestHeader("RequestVerificationToken",
                    $('input:hidden[name="__RequestVerificationToken"]').val());

            },
            success: function (data, textStatus, xhr) {
                // check for a filename
                var filename = "";
                var disposition = xhr.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 a = document.createElement('a');
                    var url = window.URL.createObjectURL(data);
                    a.href = url;
                    a.download = filename;
                    document.body.append(a);
                    a.click();
                    a.remove();
                    window.URL.revokeObjectURL(url);
                }
                else {
                    getErrorToastMessage("Production file for order line " + list[i].orderLineId + " does not exist");
                }
                i = i + 1;
                if (i < max) {
                    DownloadFile(list);
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {

            },
            complete: function () {
                if(i===max)
                hidePreloader();
            }
        });
    }

C#MVC:

 [HttpPost]
 [ValidateAntiForgeryToken]
public IActionResult OpenFile(OrderLineSimpleModel model)
        {
            byte[] file = null;

            try
            {
                if (model != null)
                {
                    //code for getting file from api - part is missing here as not important for this example
                    file = apiHandler.Get<byte[]>(downloadApiUrl, token);

                    var contentDispositionHeader = new System.Net.Mime.ContentDisposition
                    {
                        Inline = true,
                        FileName = fileName
                    };
                    //    Response.Headers.Add("Content-Disposition", contentDispositionHeader.ToString() + "; attachment");
                    Response.Headers.Add("Content-Type", "application/pdf");
                    Response.Headers.Add("Content-Disposition", "attachment; filename=" + fileName);
                    Response.Headers.Add("Content-Transfer-Encoding", "binary");
                    Response.Headers.Add("Content-Length", file.Length.ToString());

                }
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "Error getting pdf", null);
                return Ok();
            }

            return File(file, System.Net.Mime.MediaTypeNames.Application.Pdf);
        }

只要您返回响应 200,Ajax 中的成功就可以使用它,您可以检查文件是否实际存在,因为在这种情况下下面的行将是错误的,您可以通知用户:

 if (disposition && disposition.indexOf('attachment') !== -1) {

f
fstrandner

为了让 Jonathan Amends answer 在 Edge 中工作,我进行了以下更改:

var blob = typeof File === 'function'
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

对此

var f = typeof File+"";
var blob = f === 'function' && Modernizr.fileapi
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

我宁愿将此作为评论发布,但我没有足够的声誉


n
netluke

还有另一种解决方案可以在 ajax 中下载网页。但我指的是必须首先处理然后下载的页面。

首先,您需要将页面处理与结果下载分开。

1) ajax 调用中只进行页面计算。

$.post("CalculusPage.php", { calculusFunction: true, ID: 29, data1: "a", data2: "b" },

       function(data, status) 
       {
            if (status == "success") 
            {
                /* 2) In the answer the page that uses the previous calculations is downloaded. For example, this can be a page that prints the results of a table calculated in the ajax call. */
                window.location.href = DownloadPage.php+"?ID="+29;
            }               
       }
);

// For example: in the CalculusPage.php

    if ( !empty($_POST["calculusFunction"]) ) 
    {
        $ID = $_POST["ID"];

        $query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID;
        ...
    }

// For example: in the DownloadPage.php

    $ID = $_GET["ID"];

    $sede = "SELECT * FROM ExamplePage WHERE id = ".$ID;
    ...

    $filename="Export_Data.xls";
    header("Content-Type: application/vnd.ms-excel");
    header("Content-Disposition: inline; filename=$filename");

    ...

我希望这个解决方案对许多人有用,就像对我一样。


A
Abhishek Sinha

如果响应是一个数组缓冲区,请在 Ajax 中的 onsuccess 事件下尝试:

 if (event.data instanceof ArrayBuffer) {
          var binary = '';
          var bytes = new Uint8Array(event.data);
          for (var i = 0; i < bytes.byteLength; i++) {
              binary += String.fromCharCode(bytes[i])
          }
          $("#some_id").append("<li><img src=\"data:image/png;base64," + window.btoa(binary) + "\"/></span></li>");
          return;
      }

其中 event.data 是在 xhr 事件的成功函数中收到的响应。


i
il0v3d0g

我需要一个与@alain-cruz 类似的解决方案,但在 nuxt/vue 中具有多个下载。我知道浏览器会阻止多个文件下载,而且我还有返回一组 csv 格式数据的 API。我一开始打算使用 JSZip,但我需要 IE 支持,所以这是我的解决方案。如果有人可以帮助我改进这一点,那就太好了,但到目前为止它对我有用。

API 返回:

data : {
  body: {
    fileOne: ""col1", "col2", "datarow1.1", "datarow1.2"...so on",
    fileTwo: ""col1", "col2"..."
  }
}

页面.vue:

<template>
  <b-link @click.prevent="handleFileExport">Export<b-link>
</template>

export default = {
   data() {
     return {
       fileNames: ['fileOne', 'fileTwo'],
     }
   },
  computed: {
    ...mapState({
       fileOne: (state) => state.exportFile.fileOne,
       fileTwo: (state) => state.exportFile.fileTwo,
    }),
  },
  method: {
    handleExport() {
      //exportFileAction in store/exportFile needs to return promise
      this.$store.dispatch('exportFile/exportFileAction', paramsToSend)
        .then(async (response) => {
           const downloadPrep = this.fileNames.map(async (fileName) => {
           // using lodash to get computed data by the file name
           const currentData = await _.get(this, `${fileName}`);
           const currentFileName = fileName;
           return { currentData, currentFileName };
         });
         const response = await Promise.all(downloadPrep);
         return response;
       })
       .then(async (data) => {
         data.forEach(({ currentData, currentFileName }) => {
           this.forceFileDownload(currentData, currentFileName);
         });
       })
       .catch(console.error);
    },
    forceFileDownload(data, fileName) {
     const url = window.URL
         .createObjectURL(new Blob([data], { type: 'text/csv;charset=utf-8;' }));
     const link = document.createElement('a');
     link.href = url;
     link.setAttribute('download', `${fileName}.csv`);
     document.body.appendChild(link);
     link.click();
   },
}

N
Neyyadupakkam Sundarasekaran

在使用 jquery 尝试了其他几种解决方案之后,我使用了 Naren Yellavula 的解决方案,并通过对脚本的少量更改使其工作。但是,jquery 不会正确下载 zip 文件。下载后无法解压文件。在我的用例中,我必须上传一个 zip 文件,该文件在 Servlet 中解压缩,在将 zip 文件下载到客户端之前再次处理和压缩文件。这是您需要在客户端执行的操作。

$('#fileUpBtn').click(function (e){ e.preventDefault(); var file = $('#fileUpload')[0].files[0]; var formdata = new FormData(); formdata. append('file', file); // 使用 XMLHttpRequest 代替 Jquery $ajax 下载 zip 文件 xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (xhttp.readyState === 4 && xhttp.status === 200) { var a = document.createElement('a'); a.href = window.URL.createObjectURL(xhttp.response); a.download = "modified_" + file.name; a.style.display = '无'; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(a.href); } }; xhttp.open("POST" , "", true); xhttp.responseType = 'blob'; xhttp.send(formdata); });


请为您的代码提供解释 - 如果没有解释,就很难理解。
说明: 1. 上传按钮的 onclick 功能 2. 按钮点击时阻止默认表单提交 3. 选择第一个附件 4. 发送到 Servlet 的带有名称,值对的表单数据 5. 在 Formdata 中设置的上传文件 6. onreadystatechange:异步响应的处理程序来自 Servlet 7. 来自 Servlet 的最终成功响应 8. a 是将来自 Servlet 的数据设置为对象的链接 9. 将对象保存为的文件名 10. 模拟链接单击以保存文件 11. 删除响应数据 12. 打开连接到 Servlet 13. 设置响应类型 14. 发送到 Servlet