在我的ember.js
webapp中,用户可以选择下载zip文件。单击该按钮时,将触发一个操作,该操作将请求发送到后端服务器,后端服务器生成zip并将其返回。理想情况下,应自动下载zip。
在我的后端端点我回来了
return Response
.ok(FileUtils.readFileToByteArray(new File(tmpZipFilename))) // tmpZipFilename is a String
.type("application/zip")
.header("Content-Disposition", "attachment; filename=\"" + finalZipFilename + "\"")
.build();
在我的前端(改编自here)
submit() {
var formData = new FormData(this);
let token = this.get('session.data.authenticated.token');
jquery.ajax({
url: `myUrl`,
data: formData,
processData: false,
contentType: false,
beforeSend: function(xhr) {xhr.setRequestHeader('Authorization', `Bearer ${token}`)},
type: 'POST',
success: function(data) {
var blob = new Blob([data], {type: 'application/zip'});
let a = document.createElement("a");
a.style = "display: none";
document.body.appendChild(a);
let url = window.URL.createObjectURL(blob);
a.href = url;
a.download = 'myFile.zip';
a.click();
window.URL.revokeObjectURL(url);
},
failure: function() {
// other stuff
}
})
}
响应标头如下:
HTTP/1.1 200 OK
X-Powered-By: Undertow/1
Cache-Control: no-store
Date: Tue, 19 Feb 2019 16:34:35 GMT
Server: WildFly/10
Content-Type: application/zip
Content-Disposition: attachment; filename="filename.zip"
Connection: close
Transfer-Encoding: chunked
我已经确认后端部分中的tmpZipFilename
正确指向了正确的zip文件。当用户点击下载按钮时,确实下载了一个名为myFile.zip
的文件。但是,下载的文件不可解压缩,并且大小与tmpZipFilename
指向的正确文件大小不同。我究竟做错了什么?
答案是简单地添加dataType: 'arraybuffer'
:
...
jquery.ajax({
url: 'myUrl',
data: formData,
processData: false,
contentType: false,
dataType: 'arraybuffer',
beforeSend: function(xhr) {
...
实际上下载文件最好的解决方案就是调用window.open
。
如果您的API返回一个实际文件,并且您正确设置了内容类型和处置,您可以在api端点的前端创建一个自然链接,或者使用标准表单而不是AJAX请求,作为文件下载会触发,就寡妇而言,你会留在同一个地方。
对于选项一:
<a href="<API_ENDPOINT>?token=<token>¶m1=something¶m2=something">download</a>
这可以通过将参数绑定到url字符串来实现。但是我建议不要使用这种方法,除非你有办法创建SIGNED SINGLE USE GET AUTHENTICATION TOKENS,因为你永远不应该在任何地方可读地发送访问令牌。
选项2是创建一个通用的HTML表单,其中操作是api端点,您可以在后面的数据中传递令牌。如果API端点简单返回正确的文件,则在提交表单时应该下载文件,并且您应该能够保持在同一页面上。
<form action="<API ENDPOINT>" method="post">
<!-- Standard input types named appropriately
</form>
这些只是几个解决方案。我个人喜欢选项1,但前提是你能安全地工作。如果您可以在后端存储和管理某种类型的随机数,请研究HMAC auth方法来创建此单一用途令牌。适用于JWT。