Javascript 使用React和Axios从Express API下载文件
使用带有Express API的React客户端时,React客户端如何下载Express API发送的文件? 问题:Javascript 使用React和Axios从Express API下载文件,javascript,reactjs,express,axios,Javascript,Reactjs,Express,Axios,使用带有Express API的React客户端时,React客户端如何下载Express API发送的文件? 问题: 如果我在浏览器栏中键入url并按enter键,则文件下载成功 但如果我使用Axios在React应用程序中调用相同的url,则不会下载该文件 Express server //用于/api/files/testfile的路由处理程序 const getFile=async(请求、恢复、下一步)=>{ //文件 常量文件名='file.csv'; const filePath
- 如果我在浏览器栏中键入url并按enter键,则文件下载成功李>
- 但如果我使用Axios在React应用程序中调用相同的url,则不会下载该文件
//用于/api/files/testfile的路由处理程序
const getFile=async(请求、恢复、下一步)=>{
//文件
常量文件名='file.csv';
const filePath=path.join(_dirname,'/../../public/',fileName);
//文件选项
常量选项={
标题:{
“x-timestamp”:Date.now(),
“x-sent”:正确,
“内容处置”:“附件;文件名=“+filename,//被忽略
“内容类型”:“文本/csv”
}
}
试一试{
res.下载(
文件路径,
文件名,
选择权
);
log(“文件发送成功!”);
}
捕获(错误){
console.error(“无法发送文件!”);
下一步(错误);
}
});
反应客户端
//当用户单击“下载为CSV”按钮时
handleDownloadFile=()=>{
axios
.得到(
`/api/files/testfile`{
responseType:'blob',
标题:{
“内容类型”:“文本/csv”,
}
}
)
。然后(响应=>{
console.log(response.headers);//不包括内容处置
log(“文件下载成功!”);
})
.catch((错误)=>{
console.error(“无法下载文件:”,错误);
});
}
我了解到这可能与内容处置
标题有关。我尝试在中进行设置(请参阅上面的代码),但标题没有发送到客户端
不受欢迎的“解决方案”:
- 在React应用程序中:创建一个新的
元素,设置它的a
属性,并通过JavaScript触发一个href
。我正在寻找一个解决方案,不需要这个JS黑客单击
- 在React应用程序中:使用
和a
代替Axios。但是,这不适合我,因为它会绕过我的axios配置设置(API url、身份验证令牌等)target=“\u blank”
a
并单击它的情况下进行下载。然而,这似乎工作得很好(这确实是流行的实用程序使用的机制,例如)
在React中构建一个粗糙的DownloadButton
组件来实现这一点非常简单。这会模拟Axios响应,否则会从头到尾工作,除非您愿意进行任何重构。我使用hook和async/await
是为了我自己的理智/清晰,但两者都不是绝对必要的。它确实使用了on-anchor标记,这在现代浏览器中有很好的支持
function getFileNameFromContentDisposition(contentDisposition) {
if (!contentDisposition) return null;
const match = contentDisposition.match(/filename="?([^"]+)"?/);
return match ? match[1] : null;
}
const DownloadButton = ({ children, fileName, loadingText }) => {
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState(null);
const handleClick = async () => {
setLoading(true);
setError(null);
let res = null;
try {
// add any additional headers, such as authorization, as the second parameter to get below
// also, remember to use responseType: 'blob' if working with blobs instead, and use res.blob() instead of res.data below
res = await axios.get(`/api/files/${fileName}`);
setLoading(false);
} catch (err) {
setLoading(false);
setError(err);
return;
}
const data = res.data; // or res.blob() if using blob responses
const url = window.URL.createObjectURL(
new Blob([data], {
type: res.headers["content-type"]
})
);
const actualFileName = getFileNameFromContentDisposition(
res.headers["content-disposition"]
);
// uses the download attribute on a temporary anchor to trigger the browser
// download behavior. if you need wider compatibility, you can replace this
// part with a library such as filesaver.js
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", actualFileName);
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
};
if (error) {
return (<div>Unable to download file: {error.message}</div>);
}
return (
<button onClick={handleClick} disabled={loading}>
{loading ? loadingText || "Please wait..." : children}
</button>
);
};
函数getFileNameFromContentDisposition(contentDisposition){
如果(!contentDisposition)返回null;
const match=contentDisposition.match(/filename=“?([^”]+)”?/);
返回匹配?匹配[1]:空;
}
const DownloadButton=({children,fileName,loadingText})=>{
常量[loading,setLoading]=React.useState(false);
const[error,setError]=React.useState(null);
const handleClick=async()=>{
设置加载(真);
设置错误(空);
设res=null;
试一试{
//添加任何附加的头,例如authorization,作为下面的第二个参数
//另外,如果使用blob,请记住使用responseType:'blob',并使用res.blob()代替下面的res.data
res=wait axios.get(`/api/files/${fileName}`);
设置加载(假);
}捕捉(错误){
设置加载(假);
设置错误(err);
返回;
}
const data=res.data;//或res.blob(),如果使用blob响应
常量url=window.url.createObjectURL(
新Blob([数据]{
类型:res.headers[“内容类型”]
})
);
const actualFileName=getFileNameFromContentDisposition(
res.headers[“内容处置”]
);
//使用临时定位上的“下载”属性触发浏览器
//下载行为。如果您需要更广泛的兼容性,可以替换此
//使用诸如filesaver.js之类的库
const link=document.createElement(“a”);
link.href=url;
setAttribute(“下载”,实际文件名);
document.body.appendChild(链接);
link.click();
link.parentNode.removeChild(link);
};
如果(错误){
返回(无法下载文件:{error.message});
}
返回(
{加载?加载文本| |“请稍候…”:儿童}
);
};
至于ExpressJS的响应标题中未显示的内容处置
,我不确定问题出在哪里。但是,根据,第二个参数是文件名,它将自动作为内容处置
标题发送,因此您不必在选项
段落中自行指定meter.是否显示其他参数?如果是,可能在重新定义它时存在冲突选项
。但是,在本地运行与您的路由类似的示例时,我没有遇到任何问题
res.download(路径[,文件名][,选项][,fn])
Express v4.16.0及以后版本支持可选选项参数
以“附件”的形式在路径处传输文件。通常,浏览器
将提示美国
axios({
url: 'http://localhost:5000/static/example.pdf',
method: 'GET',
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', 'file.pdf');
document.body.appendChild(link);
link.click();
});
npm install --save js-file-download
import download from 'js-file-download';
downloadFile = () => {
axios.get("localhost:3000/route/path/url")
.then(resp => {
download(resp.data, fileName);
});
}