我已经在SO中通过了一些建议的解决方案,但是没有成功。这就是问题。使用从API端点检索的数据生成Blob时,我想强制浏览器下载Blob。到目前为止,我已经尝试了三种解决方案,但是没有一种有效。代码如下。请注意,我在代码中添加了一些注释以进一步解释。
const FILE_NAME_REGEX = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
export function Download(url) {
return APICall.get(url).then(response => {
const disposition = response.request.getResponseHeader('Content-Disposition');
//^This line gives the 'Refused to get unsafe header "Content-Disposition"' error, so next few lines won't execute and part with generating anchor is not used, but the part of code under the 'else' branch.
if (disposition && disposition.indexOf('attachment') !== -1) {
const matches = FILE_NAME_REGEX.exec(disposition);
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, '');
}
}
const type = response.request.getResponseHeader('Content-Type');
const blob = new Blob([response.data], { type });
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, filename);
} else {
const URL = window.URL || window.webkitURL;
const downloadUrl = URL.createObjectURL(blob);
if (filename) {
const a = document.createElement('a');
if (typeof a.download === 'undefined') {
window.location = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
} else {
// 1. soultion
window.location = downloadUrl;
}
setTimeout(() => {
URL.revokeObjectURL(downloadUrl);
}, 100);
}
});
}
// 2.解决方案
const ifrmDownloader = document.createElement('iframe');
ifrmDownloader.setAttribute('src', downloadUrl);
ifrmDownloader.style.width = '0px';
ifrmDownloader.style.height = '0px';
document.body.appendChild(ifrmDownloader);
// 3.解决方案
window.open(downloadUrl,_blank);
解决方案不起作用,因为它打开了另一个选项卡并返回空的小方块而不是文件。这可能是由于打开新选项卡时丢失了临时Blob。
解决方案根本行不通,我也不知道为什么。将iframe添加到dom中,并将请求记录在开发者控制台下的“网络”标签中,但不会触发下载。可能与解决方案1中的原因相同。此外,还会记录此消息:
资源被解释为文档,但以MIME类型image / png传输:“ blob:https://example.com/e53bf47b-7f39-4758-b3dd-3cc2df5889ad”。
我觉得这里缺少什么,但是呢?
答案 0 :(得分:1)
您的代码被设置为仅在设置filename
变量时下载文件。
仅当您能够读取Content-Disposition标头时(即仅在同源时)才设置此变量。
因此,简单的解决方法是在无法从标头获取filename
的情况下设置伪值,因此,filename
总是被设置,并且您总是尝试以insted的方式下载它只是在新页面中打开它。
更聪明的解决方法是尝试从url
变量中解析文件名,但是在不知道可能的URL格式的情况下,很难为您创建一个。
const FILE_NAME_REGEX = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
export function Download(url) {
return APICall.get(url).then(response => {
const disposition = response.request.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
const matches = FILE_NAME_REGEX.exec(disposition);
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, '');
}
}
if (!filename) {
// getFileNameFromURL(url);
filename = 'dummy.png';
}
const type = response.request.getResponseHeader('Content-Type');
const blob = new Blob([response.data], {
type
});
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, filename);
} else {
const URL = window.URL || window.webkitURL;
const downloadUrl = URL.createObjectURL(blob);
// if (filename) { // always truthy
const a = document.createElement('a');
if (typeof a.download === 'undefined') {
window.location = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
// }
/* else { // useless
window.location = downloadUrl;
}
*/
setTimeout(() => {
URL.revokeObjectURL(downloadUrl);
}, 100);
}
});
}
答案 1 :(得分:0)
关于我发布的问题,这里是我所发现的。实际上有两个问题:
Content-Disposition 标头被拒绝,因此没有文件名或发布信息。根据@Kaiido的回复,这是通过从URL中提取文件名并添加',png'来实现的。例如。
const chunks = extendedURl.split('/');
const pngExtension = '.png';
const baseName = chunks[chunks.length - 1].split('?')[0];
filename = `${baseName}${pngExtension}`;
直到今天我才意识到的更严重的问题是,当调用axios的 Get 方法时, responseType 未设置为' arraybuffer '。
因此,我能够下载“损坏”的文件。这个问题帮助我弄清楚实际问题出在哪里:File is corrupted while downloading using angular $http
一旦为调用的 Get 方法提供了' responseType:“ arraybuffer” ',文件开始显得正常,而不是“损坏”。
总而言之,在调用返回 FileStreamResult 的.net Core Web API端点时,为了能够在您的FE JavaScript中创建 Blob ,您应该明确将responseType设置为“ arraybuffer”,如下所示:
axios.get(extendedURl, { responseType: 'arraybuffer' }).then(response =>...