使用Fetch API强制下载文件

14

我正在使用fetch API调用API从服务器下载Excel文件,但它没有强制浏览器进行下载,以下是我的头部响应:

HTTP/1.1 200 OK Content-Length: 168667 
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 
Server: Microsoft-IIS/8.5 
Content-Disposition: attachment; filename=test.xlsx 
Access-Control-Allow-Origin: http://localhost:9000 
Access-Control-Allow-Credentials: true 
Access-Control-Request-Method: POST,GET,PUT,DELETE,OPTIONS 
Access-Control-Allow-Headers: X-Requested-With,Accept,Content-Type,Origin 
Persistent-Auth: true 
X-Powered-By: ASP.NET 
Date: Wed, 24 May 2017 20:18:04 GMT

以下是我用来调用API的代码:

this.httpClient.fetch(url, {
    method: 'POST',
    body: JSON.stringify(object),
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    }
})

你可能想使用 https://stackoverflow.com/posts/44168090/edit 来更新/编辑你的问题,以添加你正在使用的代码片段来尝试下载文件。 - sideshowbarker
你是如何“调用API”的? - Tom
this.httpClient.fetch(url, { method: 'POST', body: JSON.stringify(object), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }) - Ahmed Saber
我找到了使用Blob方法下载的方法,但是否有其他强制下载的方式? - Ahmed Saber
4
作为一种安全特性,JavaScript通过AJAX无法开始将文件下载到您的计算机上,因为它没有访问权限来在您的计算机上写入文件。 - Tom
请返回已翻译的文本 - djvg
5个回答

26

浏览器不会显示下载的通常交互(显示“另存为…”对话框等),除非您导航到该资源。用示例更容易说明差异:

  1. window.location='http://mycompany.com/'
  2. 通过XHR/Fetch在后台加载http://mycompany.com/

在1中,浏览器将加载页面并显示其内容。在2中,浏览器将加载原始数据并将其返回给您,但您必须自行显示它。

您需要使用文件进行类似的操作。您有原始数据,但是您必须自己“显示”它。为此,您需要为下载的文件创建一个对象URL并导航到它:

this.httpClient
    .fetch(url, {method, body, headers})
    .then(response => response.blob())
    .then(blob => URL.createObjectURL(blob))
    .then(url => {
        window.open(url, '_blank');
        URL.revokeObjectURL(url);
    });

这个代码会获取响应,将其作为blob读取,创建一个对象URL,打开它(在新标签页中),然后撤销该URL。

有关对象URL的更多信息: https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL


这个答案将会打开一个新的标签页来预览图片。但是因为它使用了 URL.revokeObjectURL(url);,所以图片无法被下载。 - Kevin Chandra

8

我的解决方案是基于 @VINEE 和 @balazska 的方案。我想避免操作 document.body 或通过 window.open(url, '_blank') 打开新标签页。

return fetch(urlEndpoint, options)
  .then((res) => res.blob())
  .then((blob) => URL.createObjectURL(blob))
  .then((href) => {
    Object.assign(document.createElement('a'), {
      href,
      download: 'filename.csv',
    }).click();
  });

5

您可以使用以下函数进行操作

download(filename) {
 fetch(url , { headers })
 .then(response => response.blob())
 .then(blob => URL.createObjectURL(blob))
 .then(uril => {
 var link = document.createElement("a");
 link.href = uril;
 link.download = filename + ".csv";
 document.body.appendChild(link);
 link.click();
 document.body.removeChild(link);
 });
}

我想下载一个CSV文件,所以我将文件名改为带有 .csv 后缀的格式。


4

有一些方便的库来解决CSV/文本下载问题,我使用了FileSaver来解决。

示例:

var saveAs = require('file-saver');

fetch('/download/urf/file', {
  headers: {
    'Content-Type': 'text/csv'
  },
  responseType: 'blob'
}).then(response => response.blob())
  .then(blob => saveAs(blob, 'test.csv'));

这个问题中,还有一个名为download.js的库。


如果你正在进行GET请求,为什么要设置请求头"Content-Type: application/pdf"? - sstur
@sstur 请求头对象拥有一个 guard 属性。该属性不会暴露给 Web,但它会影响允许在头部对象上进行哪些变异操作。详见 https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch#Headers。 - Conrado Fonseca
我仍然认为这并不能解释为什么你会在没有主体的GET请求上设置Content-Type。在这种情况下,Content-Type的含义/目的是什么? - sstur

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接