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

IT技术 javascript fetch fetch-api
2021-02-06 08:15:03

如果我想下载一个文件,我应该在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(...);
}

注意代码在客户端。

6个回答

编辑: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 希望将链接元素附加到 DOM。这在 Chrome 或 Safari 中不是强制性的。
2021-03-17 08:15:03
我不同意使用库是更好的答案:)。使用外部库并不总是一种选择,如果是,那么查找库很容易。这不值得回答,这可能是为什么尽管接受的答案比接受的答案大 2 岁,但您的答案的选票却比接受的答案多。
2021-03-24 08:15:03
@AndrewSimontsev 很棒的提示,感谢您的输入!我编辑了回复,让我知道它是否正确。在我的代码上测试过,看起来没问题!
2021-04-06 08:15:03
还查看了 downloadjs 上的代码,他们使用与 temp <a/> 相同的方法来保存文件。所以图书馆不一定会做得更好。
2021-04-08 08:15:03
最后调用 URL.revokeObjectURL 以避免内存泄漏是有意义的。
2021-04-10 08:15:03

这更短更高效,没有库只获取 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);
  });

@LucasMatos 我向选项对象添加了“Content-Disposition”标头,并且在网络选项卡中检查文件时确实获得了正确的标头,但是随后创建了 blob 并丢弃了名称,因此我最终生成了一个随机生成的文件名称。您知道如何使用您的解决方案将名称传递给 blob 吗?
2021-03-15 08:15:03
有没有办法设置文件名?
2021-03-25 08:15:03
2021-03-26 08:15:03
@LucasMatos 我尝试编辑您的答案,但队列已满。为了为正在下载的文件断言名称是添加额外的一行: var file = new File([blob], "filename.extension"); 文件 = window.URL.createObjectURL(file);
2021-03-27 08:15:03
是的,您可以将 Content-Disposition 添加到 Headers obj,这里是文档developer.mozilla.org/en-US/docs/Web/HTTP/Headers/...
2021-04-08 08:15:03

我通过使用download.jsblob.

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

很好,只有一件事:是否可以从服务器响应中获取文件名,以便让用户以其真实姓名下载它?
2021-03-13 08:15:03
请记住,浏览器当前的 blob 大小限制约为 500mb
2021-03-30 08:15:03
@Phate 要这样做,您应该从服务器传递一个对象,该对象不仅包含数据还包含名称。一旦你这样做,你可以单独处理它的领域,但为此你应该在这里看到其他答案(据我所知).blob方法是特定的对象之一,它fetch解决
2021-04-05 08:15:03

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");

使用下载js。这将从标题中解析文件名。

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;
2021-03-18 08:15:03
这主要是有效的。除了 Apple 落后于时代并且您的正则表达式在 Safari 上失败。:( aizquier 的回答有效。
2021-03-21 08:15:03
Firefox 不支持filename.match(),我将该部分替换为:filename = response.headers.get("content-disposition").split(";")[1].split('"')[1];此外,服务器必须声明标头Access-Control-Expose-Headers: Content-Disposition,以便浏览器读取content-disposition标头。
2021-04-07 08:15:03