从blob保存文件:使用没有重定向的javascript的http

问题描述 投票:0回答:2

我已经在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);
  1. 解决方案不起作用,因为它打开另一个选项卡并返回空的小方块而不是文件。这可能是由于新标签打开时丢失的临时blob。
  2. 解决方案根本不起作用,我不知道为什么。 iframe被添加到dom中,请求被记录在开发者控制台下的“网络”选项卡中,但没有下载任何内容。可能与解决方案中的原因相同1.此外,还会记录此消息:

资源解释为Document但使用MIME类型image / png传输:“blob:https://example.com/e53bf47b-7f39-4758-b3dd-3cc2df5889ad”。

  1. 解决方案也不起作用。首先,浏览器被阻止弹出,并且在允许弹出后,不启动文件下载。

我觉得这里缺少一些东西,但是什么?

javascript ajax image browser webclient-download
2个回答
1
投票

您的代码设置为仅在设置filename变量时下载文件。

仅当您能够读取Content-Disposition标头时(即仅在同源时)才设置此变量。

所以简单的解决方法就是当你没有从Headers中获取它时为filename设置一个虚拟值,这样,filename总是被设置,你总是尝试下载它只是在新页面中打开它。 更聪明的解决方法是尝试从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);
    }
  });
}

0
投票

以下是我发现的问题。实际上有两个问题:

  1. Content-Disposition标题被拒绝,因此没有文件名和处置信息可用。根据@Kaiido的回复,通过从URL中提取文件名并添加',png'来弥补这一点。例如。 const chunks = extendedURl.split('/'); const pngExtension = '.png'; const baseName = chunks[chunks.length - 1].split('?')[0]; filename = `${baseName}${pngExtension}`;
  2. 直到今天我才意识到的更严重的问题是,当调用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 =>...
© www.soinside.com 2019 - 2024. All rights reserved.