处理来自ajax文章的文件下载

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

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

编辑:

显然,这是无法完成的,但正如接受的答案所建议的那样,有一个简单的解决方法。 对于将来遇到此问题的任何人,以下是我如何解决它的方法:

$.ajax({
    type: "POST",
    url: url,
    data: params,
    success: function(response, status, request) {
        var disp = request.getResponseHeader('Content-Disposition');
        if (disp && disp.search('attachment') != -1) {
            var form = $('<form method="POST" action="' + url + '">');
            $.each(params, function(k, v) {
                form.append($('<input type="hidden" name="' + k +
                        '" value="' + v + '">'));
            });
            $('body').append(form);
            form.submit();
        }
    }
});

所以基本上,只需生成一个与AJAX请求中使用的相同参数并提交它的HTML表单。


创建一个表单,使用POST方法,提交表单 - 不需要iframe。 当服务器页面响应请求时,为该文件的MIME类型编写一个响应头文件,并且它会显示一个下载对话框 - 我已经多次完成了这一步。

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


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

编辑2017-09-28:已更新为在可用时使用文件构造函数,因此它适用于Safari> = 10.1。

编辑2015-10-16:jQuery ajax无法正确处理二进制响应(无法设置responseType),因此最好使用纯XMLHttpRequest调用。

var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
    if (this.status === 200) {
        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 type = xhr.getResponseHeader('Content-Type');

        var blob = typeof File === 'function'
            ? new File([this.response], filename, { type: type })
            : new Blob([this.response], { type: type });
        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 = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location = downloadUrl;
            }

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

这是使用jQuery.ajax的旧版本。 当响应转换为一些字符串时,它可能会破坏二进制数据。

$.ajax({
    type: "POST",
    url: url,
    data: params,
    success: function(response, 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, '');
        }

        var type = xhr.getResponseHeader('Content-Type');
        var blob = new Blob([response], { type: type });

        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 = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location = downloadUrl;
            }

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

你使用什么服务器端语言? 在我的应用程序中,我可以通过在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 alread在他的评论中表示,它不会离开当前页面。

这是关于设置正确的标题,所以我相信你会找到一个合适的解决方案来使用你使用的服务器端语言,如果它不是PHP的话。

处理响应客户端

假设您已经知道如何进行AJAX调用,则在客户端执行对服务器的AJAX请求。 然后,服务器会生成一个链接,从中可以下载该文件,例如您希望指向的“前进”URL。 例如,服务器回应:

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

在处理响应时,你在你的body中注入一个iframe ,并将iframe的SRC设置为你刚才收到的URL(为了简化示例,使用jQuery):

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

如果您已经设置了正确的标题,iframe将强制下载对话框,而无需将浏览器从当前页面导航。

注意

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

链接地址: http://www.djcxy.com/p/8057.html

上一篇: Handle file download from ajax post

下一篇: Differences between contentType and dataType in jQuery ajax function