Pav*_*dic 373 javascript ajax jquery
我有一个javascript应用程序,它将ajax POST请求发送到某个URL.响应可能是JSON字符串,也可能是文件(作为附件).我可以在我的ajax调用中轻松检测Content-Type和Content-Disposition,但是一旦我检测到响应包含文件,我该如何让客户端下载它?我在这里读过很多类似的帖子,但没有一个提供我正在寻找的答案.
请,请不要发布答案,建议我不应该使用ajax或者我应该重定向浏览器,因为这都不是一个选项.使用纯HTML表单也不是一种选择.我需要的是向客户端显示下载对话框.可以这样做,怎么做?
编辑:
显然,这不可能做到,但有一个简单的解决方法,如接受的答案所示.对于今后遇到这个问题的人来说,我的解决方法如下:
__PRE__
所以基本上,只需生成一个HTML表单,其中包含与AJAX请求中使用的相同的参数并提交它.
Jon*_*end 504
不要放弃这么快,因为这可以使用FileAPI的一部分(在现代浏览器中)完成:
编辑2017-09-28:更新为使用File构造函数,因此它可以在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;
if (typeof File === 'function') {
try {
blob = new File([this.response], filename, { type: type });
} catch (e) { /* Edge */ }
}
if (typeof blob === 'undefined') {
blob = 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));
Run Code Online (Sandbox Code Playgroud)
这是使用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
}
}
});
Run Code Online (Sandbox Code Playgroud)
小智 110
创建一个表单,使用POST方法,提交表单 - 不需要iframe.当服务器页面响应请求时,为文件的mime类型写一个响应头,它将显示一个下载对话框 - 我已经多次这样做了.
您需要内容类型的应用程序/下载 - 只需搜索如何为您正在使用的任何语言提供下载.
Nar*_*ula 35
我遇到了同样的问题并成功解决了它.我的用例是这样的.
"将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));
});
Run Code Online (Sandbox Code Playgroud)
上面的代码片段正在执行以下操作
在这里,我们需要在服务器端仔细设置一些东西.我在Python Django HttpResponse中设置了几个标题.如果使用其他编程语言,则需要相应地设置它们.
# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
Run Code Online (Sandbox Code Playgroud)
由于我在这里下载xls(excel),我将contentType调整为高于1.您需要根据文件类型进行设置.您可以使用此技术下载任何类型的文件.
Rob*_*len 31
你使用什么服务器端语言?在我的应用程序中,我可以通过在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;
Run Code Online (Sandbox Code Playgroud)
这实际上会将浏览器"重定向"到这个下载页面,但正如@ahren alread在评论中所说,它不会离开当前页面.
这都是关于设置正确的标题所以我相信如果它不是PHP,你会发现你正在使用的服务器端语言的合适解决方案.
假设您已经知道如何进行AJAX调用,则在客户端执行对服务器的AJAX请求.然后,服务器生成可以下载此文件的链接,例如您要指向的"转发"URL.例如,服务器响应:
{
status: 1, // ok
// unique one-time download token, not required of course
message: 'http://yourwebsite.com/getdownload/ska08912dsa'
}
Run Code Online (Sandbox Code Playgroud)
在处理响应时,你iframe在你的身体中注入一个并将你iframe的SRC 设置为你刚才收到的URL(为了这个例子,使用jQuery):
$("body").append("<iframe src='" + data.message +
"' style='display: none;' ></iframe>");
Run Code Online (Sandbox Code Playgroud)
如果您已如上所示设置了正确的标题,那么iframe将强制执行下载对话框,而不会将浏览器导航离开当前页面.
与您的问题有关的额外补充; 我认为在使用AJAX技术请求内容时总是返回JSON.收到JSON响应后,您可以决定客户端如何处理它.也许,例如,稍后您希望用户单击URL的下载链接而不是直接强制下载,在当前设置中,您必须更新客户端和服务器端才能执行此操作.
Tim*_*ler 22
对于那些从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();
});
Run Code Online (Sandbox Code Playgroud)
May*_*ala 17
以下是我如何使用这个工作 /sf/answers/1929476741/
$.ajax({
url: '<URL_TO_FILE>',
success: function(data) {
var blob=new Blob([data]);
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="<FILENAME_TO_SAVE_WITH_EXTENSION>";
link.click();
}
});Run Code Online (Sandbox Code Playgroud)
$.ajax({
url: '<URL_TO_FILE>',
success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
});Run Code Online (Sandbox Code Playgroud)
Jos*_*AGO 12
我看到你已经找到了一个解决方案,但我只是想添加一些信息,这可能有助于某些人试图通过大的POST请求实现同样的目的.
几周前我遇到了同样的问题,确实不可能通过AJAX实现"干净"下载,Filament Group创建了一个jQuery插件,它完全按照你已经发现的方式工作,它被称为jQuery文件然而,下载此技术有一个缺点.
如果您通过AJAX发送大量请求(比如文件+ 1MB),则会对响应性产生负面影响.在慢速Internet连接中,您必须等待很长时间才能发送请求,并等待文件下载.它不像是瞬间"点击"=>"弹出"=>"下载开始".它更像是"click"=>"等待数据发送"=>"等待响应"=>"下载开始",这使得它看起来文件的大小加倍,因为你必须等待发送请求通过AJAX并将其作为可下载文件取回.
如果您使用的文件小于1MB,您将不会注意到这一点.但正如我在自己的应用程序中发现的那样,对于更大的文件大小,它几乎无法忍受.
我的应用程序允许用户导出动态生成的图像,这些图像通过POST64格式的POST请求发送到服务器(这是唯一可能的方式),然后处理并以.png,.jpg文件,base64的形式发送给用户图像的字符串+ 1MB是巨大的,这迫使用户等待文件开始下载所需的时间.在慢速的互联网连接中,它可能真的很烦人.
我的解决方案是暂时将文件写入服务器,一旦准备就绪,动态生成一个按钮形式的文件链接,该按钮在"请稍候..."和"下载"状态之间切换,同时时间,在预览弹出窗口中打印base64图像,以便用户"右键单击"并保存.这使得所有等待时间对用户来说更加可以忍受,并且还可以加快速度.
2014年9月30日更新:
自从我发布这个月以来已经过了几个月,最后我找到了一个更好的方法来加快使用big base64字符串的速度.我现在将base64字符串存储到数据库中(使用longtext或longblog字段),然后我通过jQuery文件下载传递其记录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;
?>
Run Code Online (Sandbox Code Playgroud)
我知道这超出了OP所要求的范围,但我觉得用我的发现更新我的答案会更好.当我在寻找我的问题的解决方案时,我阅读了许多"从AJAX POST数据下载"的帖子,这些帖子没有给我我想要的答案,我希望这些信息可以帮助那些想要实现这样的事情的人.
Ala*_*ruz 10
对于那些寻求更现代方法的人,您可以使用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();
})
Run Code Online (Sandbox Code Playgroud)
我相信这种方法比其他XMLHttpRequest解决方案更容易理解。此外,它具有与该jQuery方法类似的语法,无需添加任何额外的库。
当然,我建议检查您正在开发的浏览器,因为这种新方法不适用于 IE。您可以在以下 [链接][1] 中找到完整的浏览器兼容性列表。
重要提示:在本例中,我将一个 JSON 请求发送到侦听给定url. 这url必须设置,在我的例子中,我假设你知道这部分。此外,请考虑您的请求工作所需的标头。由于我发送的是 JSON,因此我必须添加Content-Type标头并将其设置为application/json; charset=utf-8,以便让服务器知道它将接收的请求类型。
小智 6
这是我使用临时隐藏表单的解决方案。
//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();
Run Code Online (Sandbox Code Playgroud)
请注意,我大量使用 JQuery,但您可以使用本机 JS 做同样的事情。
我想指出在接受的答案中使用该技术时出现的一些困难,即使用表格帖子:
您无法在请求上设置标头.如果您的身份验证架构涉及标头,在Authorization标头中传递了Json-Web-Token,您将必须找到其他方式来发送它,例如作为查询参数.
您无法确定请求何时完成.好吧,你可以使用一个响应设置的cookie,就像jquery.fileDownload一样,但它是完美的FAR.它不适用于并发请求,如果响应永远不会到达,它将会中断.
如果服务器响应错误,则用户将被重定向到错误页面.
您只能使用表单支持的内容类型.这意味着您无法使用JSON.
我最终使用在S3上保存文件的方法并发送预先签名的URL来获取文件.
| 归档时间: |
|
| 查看次数: |
414185 次 |
| 最近记录: |