如何使用window.fetch下载文件?


80

如果要下载文件,在then下面的方框中应该怎么做?

function downloadFile(token, fileId) {
  let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;
  return fetch(url, {
    method: 'GET',
    headers: {
      'Authorization': token
    }
  }).then(...);
}

请注意,代码在客户端。


1
将下载属性添加到具有URL的链接该怎么办 https://www.googleapis.com/drive/v2/files/${fileId}?alt=media
Arjun 2015年

@Arjun如何将令牌添加到请求的标头?
anhtv13

@ anhtv13对不起,我不明白你的问题。用我的原始评论,我建议的解决方案是使用JavaScript创建<a>带有download属性的元素并模拟对该元素的单击。请参阅Zibri的答案(顺便说一句,在我发表评论后已发布)。
Arjun

api是安全的,您不仅可以转到URL并下载,还需要将令牌传递给标头。类似于“授权”:“承载者” + <令牌>。在这种情况下,如何将令牌添加到标头?
anhtv13

Answers:


75

编辑:syg的答案更好。只需使用downloadjs库。

我提供的答案在Chrome上很好用,但是在Firefox和IE上,您需要此代码的一些不同变体。最好使用库。


我有类似的问题(需要传递授权标头来下载文件,因此解决方案无济于事)。

但是基于答案,您可以createObjectURL使浏览器保存Fetch API下载的文件。

getAuthToken()
    .then(token => {
        fetch("http://example.com/ExportExcel", {
            method: 'GET',
            headers: new Headers({
                "Authorization": "Bearer " + token
            })
        })
        .then(response => response.blob())
        .then(blob => {
            var url = window.URL.createObjectURL(blob);
            var a = document.createElement('a');
            a.href = url;
            a.download = "filename.xlsx";
            document.body.appendChild(a); // we need to append the element to the dom -> otherwise it will not work in firefox
            a.click();    
            a.remove();  //afterwards we remove the element again         
        });
    });

@David我更新了答案。现在应该也可以在FF中使用。问题是,FF希望将link元素附加到DOM。在Chrome或Safari中这不是强制性的。
messerbill

2
最后调用URL.revokeObjectURL可以避免内存泄漏。
安德鲁·西蒙采夫

@AndrewSimontsev大提示,感谢您的输入!我编辑了回复,让我知道这样是否正确。经过我的代码测试,看起来还可以!
jbarradas

2
我不同意使用库是一个更好的答案:)。使用外部库并非始终是一种选择,当可以时,查找库很容易。这不是值得的答案,这很可能是您的答案尽管被接受的年龄比2岁大,但其投票数却比接受的答案多的原因。
聚合

47

我通过使用download.js和临时解决了这个问题blob

let download = require('./download.min');

...

function downloadFile(token, fileId) {
  let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;
  return fetch(url, {
    method: 'GET',
    headers: {
      'Authorization': token
    }
  }).then(function(resp) {
    return resp.blob();
  }).then(function(blob) {
    download(blob);
  });
}

它适用于小文件,但可能不适用于大文件。我想我应该进一步挖掘Stream


8
只要记住当前浏览器的Blob大小限制约为
500mb

很好,只有一件事:是否有可能从服务器响应中获取文件名,以便让用户以其真实姓名下载文件?
Phate

我找到这个问题的唯一解决方案。谢谢!
caravana_942

46

这更短和更有效,没有库仅获取API

const url ='http://sample.example.file.doc'
const authHeader ="Bearer 6Q************" 

const options = {
  headers: {
    Authorization: authHeader
  }
};
 fetch(url, options)
  .then( res => res.blob() )
  .then( blob => {
    var file = window.URL.createObjectURL(blob);
    window.location.assign(file);
  });


4
有什么办法设置文件名?
安东

2
是的,你可以添加内容处置的头OBJ,这里是文档developer.mozilla.org/en-US/docs/Web/HTTP/Headers/...
卢卡斯·马托斯

这很漂亮。
ryanpcmcquen

1
@LucasMatos我在选项对象中添加了“ Content-Disposition”标头,并且在“网络”选项卡中检查该文件时,确实获得了正确的标头,但是随后创建了Blob,并丢弃了该名称,因此最终生成了一个随机数名称。您知道如何将名称传递给解决方案吗?
miran80 '20


11

function download(dataurl, filename) {
  var a = document.createElement("a");
  a.href = dataurl;
  a.setAttribute("download", filename);
  a.click();
  return false;
}

download("data:text/html,HelloWorld!", "helloWorld.txt");

要么:

function download(url, filename) {
fetch(url).then(function(t) {
    return t.blob().then((b)=>{
        var a = document.createElement("a");
        a.href = URL.createObjectURL(b);
        a.setAttribute("download", filename);
        a.click();
    }
    );
});
}

download("https://get.geojs.io/v1/ip/geo.json","geoip.json")
download("data:text/html,HelloWorld!", "helloWorld.txt");


8

使用dowloadjs。这将从头文件中解析文件名。

fetch("yourURL", {
    method: "POST",
    body: JSON.stringify(search),
    headers: {
        "Content-Type": "application/json; charset=utf-8"
    }
    })
    .then(response => {
        if (response.status === 200) {
            filename = response.headers.get("content-disposition");
            filename = filename.match(/(?<=")(?:\\.|[^"\\])*(?=")/)[0];
            return response.blob();
        } else {
        return;
        }
    })
    .then(body => {
        download(body, filename, "application/octet-stream");
    });
};

这通常可以工作,但是我最终还是使用了另一个答案中的正则表达式。所以...fileName = fileName.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1] ? fileName.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1] : fileName;
thargenediad

1
Firefox不支持filename.match(),我将该部分替换为:filename = response.headers.get("content-disposition").split(";")[1].split('"')[1];。此外,服务器必须声明标头Access-Control-Expose-Headers: Content-Disposition,以允许浏览器读取content-disposition标头。
aizquier

3

这是一个使用node-fetch进行查找的示例。

reportRunner({url, params = {}}) {
    let urlWithParams = `${url}?`
    Object.keys(params).forEach((key) => urlWithParams += `&${key}=${params[key]}`)
    return fetch(urlWithParams)
        .then(async res => ({
            filename: res.headers.get('content-disposition').split('filename=')[1],
            blob: await res.blob()
        }))
        .catch(this.handleError)
}

它将保存到哪里?@Michael Hobbs
FabricioG

该函数返回一个对象{filename,blob},您可以使用fs.writefile将文件保存到磁盘。
Michael Hobbs

1

我尝试了window.fetch,但最终由于我的REACT应用而变得复杂

现在我只是更改window.location.href并添加诸如jsonwebtoken和的查询参数other stuff


///==== client side code =====
var url = new URL(`http://${process.env.REACT_APP_URL}/api/mix-sheets/list`);
url.searchParams.append("interval",data.interval);
url.searchParams.append("jwt",token)

window.location.href=url;

// ===== server side code =====

// on the server i set the content disposition to a file
var list = encodeToCsv(dataToEncode);
res.set({"Content-Disposition":`attachment; filename=\"FileName.csv\"`});
res.status(200).send(list)

最终结果实际上是非常不错的,窗口发出请求并下载文件,并且事件切换不会将页面移开,就好像window.location.href调用就像是低调fetch()调用一样。


1

根据其他一些答案,您绝对可以使用window.fetch和download.js下载文件。但是,将window.fetch与blob一起使用会对浏览器施加的内存造成限制,而download.js也具有其兼容性限制

如果您需要下载大文件,则不想将其放在客户端的内存中以给浏览器施加压力,对吗?相反,您可能更喜欢通过流下载它。在这种情况下,使用HTML链接下载文件是最好/最简单的方法之一,尤其是对于通过流下载大文件的情况。

第一步:创建链接元素并设置其样式

您可以使链接不可见,但仍可操作。

HTML:

<a href="#" class="download-link" download>Download</a>

CSS:

.download-link {
  position: absolute;
  top: -9999px;
  left: -9999px;
  opacity: 0;
}

第二步:设置href链接的,并触发click事件

的JavaScript

let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;

const downloadLink = document.querySelector('.download-link')
downloadLink.href = url + '&ts=' + new Date().getTime() // Prevent cache
downloadLink.click()

注意事项

  • 您可以根据需要动态生成link元素。
  • 这种方法对于通过流下载在服务器端动态生成的大文件特别有用。

如何在HTML的内联上方创建CSS?
JeffR

1
@JeffR,它想要这样的东西:<a href="#" class="download-link" style="position: absolute; top: -9999px; left: -9999px; opacity: 0" download>Download</a>class这里的属性仅供querySelector在JS代码中使用。
榆次

当我执行“ downloadLink.click()”行时,其余的HTML代码都会消失。有什么理由吗?如果我注释掉“ downloadLink.click()”,而显示“下载”链接,则所有html都可以正常工作。有任何想法吗?
JeffR

@JeffR可能与aHTML元素的download属性有关。此属性指示浏览器下载URL而不是导航到URL,因此将提示用户将URL保存为本地文件。此属性仅适用于同源URL。也可以尝试不使用该'&ts=' + new Date().getTime()部分,以查看它是否对您的情况有所影响。
榆次
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.