我从API接收文件的URL作为响应。当用户点击下载按钮时,应该直接下载文件而不是在新标签页中打开文件预览。如何在React.js中实现?
我从API接收文件的URL作为响应。当用户点击下载按钮时,应该直接下载文件而不是在新标签页中打开文件预览。如何在React.js中实现?
tldr; 从URL获取文件,将其存储为本地Blob,在DOM中注入链接元素并单击它以下载Blob。
我有一个PDF文件,它存储在Cloudfront URL后面的S3中。我希望用户能够单击按钮并立即启动下载,而不会弹出具有PDF预览的新标签页。通常,如果文件托管在与用户当前所在网站不同的URL上,则许多浏览器出于用户安全原因会阻止立即下载。如果使用此解决方案,请不要在没有用户点击按钮有意下载的情况下启动文件下载。
为了绕过此问题,我需要从URL获取文件,避开任何CORS策略来保存本地Blob,然后该Blob将是下载文件的源。在下面的代码中,请确保您替换自己的fileURL
、Content-Type
和FileName
。
fetch('https://cors-anywhere.herokuapp.com/' + fileURL, {
method: 'GET',
headers: {
'Content-Type': 'application/pdf',
},
})
.then((response) => response.blob())
.then((blob) => {
// Create blob link to download
const url = window.URL.createObjectURL(
new Blob([blob]),
);
const link = document.createElement('a');
link.href = url;
link.setAttribute(
'download',
`FileName.pdf`,
);
// Append to html link element page
document.body.appendChild(link);
// Start download
link.click();
// Clean up and remove the link
link.parentNode.removeChild(link);
});
这个解决方案涉及到如何从URL获取blob和使用CORS代理的解决方案的引用。getting a blob from a URL 和 using a CORS proxy.
更新截至2021年1月31日,托管在Heroku服务器上的cors-anywhere演示只允许有限的用于测试目的的使用,并且不能用于生产应用程序。您需要按照cors-anywhere或cors-server的指示自己托管cors-anywhere服务器。
new Blob([blob])
?你已经有一个 blob
中的 Blob
了。 - Phil这与React无关。但是,您可以在锚点<a>
元素上使用download
属性,告诉浏览器下载该文件。
<a href='/somefile.txt' download>Click to download</a>
这并不是所有浏览器都支持的:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a
download
属性在95%的浏览器中得到支持:https://caniuse.com/download,因此... ♂️ - fullStackChris<Link to="/files/myfile.pdf" target="_blank" download>Download</Link>
当 /files/myfile.pdf
存在于您的 public
文件夹中时。
在前端触发浏览器下载不可靠。
你应该在服务器上创建一个端点,当调用它时,它将响应正确的响应头,从而触发浏览器下载。
前端代码只能做这么多。例如,'download'属性可能会根据浏览器和文件类型打开新标签页。
你需要查看的响应头是Content-Type
和Content-Disposition
。你应该参考这个答案来获取更详细的解释。
解决方法(适用于React JS、Next JS)
你可以使用js-file-download,这是我的示例:
import axios from 'axios'
import fileDownload from 'js-file-download'
...
handleDownload = (url, filename) => {
axios.get(url, {
responseType: 'blob',
})
.then((res) => {
fileDownload(res.data, filename)
})
}
...
<button onClick={() => {this.handleDownload('https://your-website.com/your-image.jpg', 'test-download.jpg')
}}>Download Image</button>
这个插件可以下载Excel和其他文件类型。
浏览器足够智能,可以检测到链接并在单击锚标签时直接下载,而无需使用下载属性。
从 API 获取文件链接后,只需使用纯 JavaScript 创建锚标签,并在动态单击后立即删除它。
const link = document.createElement('a');
link.href = `your_link.pdf`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
your_link.pdf
; link.download = yourFileName
; 非常感谢。 - Pankaj这是我在React中的实现方式:
import MyPDF from '../path/to/file.pdf';
<a href={myPDF} download="My_File.pdf"> Download Here </a>
重要的是使用download="name_of_file_you_want.pdf"
覆盖默认文件名,否则在下载时文件名称将添加散列号。
当使用a
标签并设置target="_blank"
时,React会存在安全问题。
我成功地让它像这样工作:
<a href={uploadedFileLink} target="_blank" rel="noopener noreferrer" download>
<Button>
<i className="fas fa-download"/>
Download File
</Button>
</a>
从'../assets/data/resume.pdf'导入简历
<a href={resume} download="YourName resume.pdf"> Download CV </a>
fetchFile(){
axios({
url: `/someurl/thefiles/${this.props.file.id}`,
method: "GET",
headers: headers,
responseType: "blob" // important
}).then(response => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute(
"download",
`${this.props.file.name}.${this.props.file.mime}`
);
document.body.appendChild(link);
link.click();
// Clean up and remove the link
link.parentNode.removeChild(link);
});
}
render(){
return( <button onClick={this.fetchFile}> Download file </button>)
}