Angular下载文件
之前MVC那一套写文件下载的时候前端直接href
,后端返回一个流式响应即可,浏览器就会自动下载。但使用前后分离后我想用之前的逻辑来着,但不是用href
,而是用angular的HttpClient
,开发环境用的--proxy-config
,用href
的话需访问后端端口,但在生产环境后端和前端是代理在同一端口的(在同一机器上。。。)
于是我开始按照之前的逻辑整,使用HttpClient
来请求数据流
// 该段代码不能实现正确下载
this.http.get(url, {responseType: 'blob' as 'blob'}).subscribe(res => {
const objUrl = URL.createObjectURL(res);
const link = document.createElement('a');
link.href = objUrl;
link.download = fileName;
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(objUrl);
})
HttpClient.get
的options参数中的responseType
不能直接{responseType: 'blob'}
这样传,回报类型错误。参见https://github.com/angular/angular/issues/18586
这样做的话(就我目前的实现)是不能都下载到正确的文件的,下载的文件内容中都是数字,可能是我对HttpClient
和Blob
对象不太了解吧,以后在研究。
现在我的需求下载的文件不会太大,我就采用了一种确定好使的方法,因为angular默认都是使用Json来和后端交互。所以我后端不返回流式响应,而返回包含文件base64字符串的json,然后在前端decode下载.
this.http.get(url).subscribe(res => {
/*
* res : {
* fileData;
* fileName;
* }
**/
const bstr = atob(res['fileData']);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const blob = new Blob([u8arr]);
const objUrl = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = objUrl;
link.download = res['fileName'];
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(objUrl);
});
这种方法下载小文件还是很不错的,但是下载大文件估计回奔溃。。。下载达文件还是使用href
直接get比较好,一般前后分离项目前后端应该不会部署在一台机器上的,但在一台机器上避免了跨域问题。
2019-01-21 14:58:23
修改
使用HttpClient
来请求数据流的方式可行,是我的实现方式不对,参考https://blog.csdn.net/shengandshu/article/details/81127279
this.http.get(url, {responseType: 'blob', observe: 'response'}).subscribe(data => {
const link = document.createElement('a');
const blob = new Blob([data.body]);
link.setAttribute('href', window.URL.createObjectURL(blob));
link.setAttribute('download', data.headers.get('Content-disposition').split('filename=')[1]);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
另附django返回文件流代码
from django.http import StreamingHttpResponse
from wsgiref.util import FileWrapper
...
# file
response = StreamingHttpResponse(FileWrapper(file, 4096))
response['Content-Length'] = file.size
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = "attachment; filename='{0}'".format(file_name)
return response