大部分答案都遗漏了几个关键点。
我会在这里尝试更深入的解释。
TLDR;
如果您正在创建a 标签链接并通过浏览器请求启动下载,那么
-
请始终致电window.URL.revokeObjectURL(url);。否则可以有
不必要的内存峰值。
-
无需使用document.body.appendChild(link); 将创建的链接附加到文档正文,防止以后不必要地删除子。
有关组件代码和更深入的分析,请进一步阅读
首先要确定您尝试从中下载数据的 API 端点是公共的还是私有的。您是否可以控制服务器?
如果服务器响应
Content-Disposition: attachment; filename=dummy.pdf
Content-Type: application/pdf
浏览器总是会尝试下载名为“dummy.pdf”的文件
如果服务器响应
Content-Disposition: inline; filename=dummy.pdf
Content-Type: application/pdf
浏览器将首先尝试打开名为“dummy.pdf”的本机文件阅读器,否则它将开始文件下载。
如果服务器以 上述两个标头中的任何一个响应
如果未设置下载属性,浏览器(至少 chrome)将尝试打开文件。如果设置,它将下载文件。在 url 不是 blob 的情况下,文件的名称将是最后一个路径参数的值。
除此之外,请记住使用来自服务器的Transfer-Encoding: chunked 从服务器传输大量数据。这将确保客户端知道在没有Content-Length 标头的情况下何时停止读取当前请求
对于私人文件
import { useState, useEffect } from "react";
import axios from "axios";
export default function DownloadPrivateFile(props) {
const [download, setDownload] = useState(false);
useEffect(() => {
async function downloadApi() {
try {
// It doesn't matter whether this api responds with the Content-Disposition header or not
const response = await axios.get(
"http://localhost:9000/api/v1/service/email/attachment/1mbdoc.docx",
{
responseType: "blob", // this is important!
headers: { Authorization: "sometoken" },
}
);
const url = window.URL.createObjectURL(new Blob([response.data])); // you can mention a type if you wish
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", "dummy.docx"); //this is the name with which the file will be downloaded
link.click();
// no need to append link as child to body.
setTimeout(() => window.URL.revokeObjectURL(url), 0); // this is important too, otherwise we will be unnecessarily spiking memory!
setDownload(false);
} catch (e) {} //error handling }
}
if (download) {
downloadApi();
}
}, [download]);
return <button onClick={() => setDownload(true)}>Download Private</button>;
}
对于公共文件
import { useState, useEffect } from "react";
export default function DownloadPublicFile(props) {
const [download, setDownload] = useState(false);
useEffect(() => {
if (download) {
const link = document.createElement("a");
link.href =
"http://localhost:9000/api/v1/service/email/attachment/dummy.pdf";
link.setAttribute("download", "dummy.pdf");
link.click();
setDownload(false);
}
}, [download]);
return <button onClick={() => setDownload(true)}>Download Public</button>;
}
很高兴知道:
-
始终控制从服务器下载文件。
-
浏览器中的 Axios 在底层使用 XHR,其中流式响应
不支持。
-
使用Axios的onDownloadProgress方法实现进度条。
-
来自服务器的分块响应不(不能)指示内容长度。因此,如果您在构建进度条时使用它们,则需要某种方式来了解响应大小。
-
<a> 标签链接只能发出 GET HTTP 请求,而不能发送标头或
cookie 到服务器(非常适合从公共端点下载)
-
浏览器请求与代码中的 XHR 请求略有不同。
参考:Difference between AJAX request and a regular browser request