![](/img/trans.png)
[英]File Downloading Error Using Express, Axios and Js-File-Download
[英]Download file from Express API using React and Axios
當使用帶有 Express API 的 React 客戶端時,React 客戶端如何下載 Express API 發送的文件?
問題:
快遞服務器
// Route handler for /api/files/testfile
const getFile = async (req, res, next) => {
// File
const fileName = 'file.csv';
const filePath = path.join(__dirname, '/../../public/', fileName);
// File options
const options = {
headers: {
'x-timestamp': Date.now(),
'x-sent': true,
'content-disposition': "attachment; filename=" + fileName, // gets ignored
'content-type': "text/csv"
}
}
try {
res.download(
filePath,
fileName,
options
);
console.log("File sent successfully!");
}
catch (error) {
console.error("File could not be sent!");
next(error);
}
});
反應客戶端
// When the user clicks the "Download as CSV" button
handleDownloadFile = () => {
axios
.get(
`/api/files/testfile`, {
responseType: 'blob',
headers: {
'Content-Type': 'text/csv',
}
}
)
.then(response => {
console.log(response.headers); // does not include content-disposition
console.log("File downloading successfully!");
})
.catch( (error) => {
console.error("File could not be downloaded:", error);
});
}
我讀到這可能與content-disposition
header 有關。 我嘗試設置(參見我上面的代碼),但 header 沒有發送到客戶端。
不受歡迎的“解決方案”:
在 React 應用程序中:創建一個新a
元素,設置其href
屬性並通過 JavaScript 觸發click
。 我正在尋找一個不需要這個 JS hack 的解決方案。
在 React 應用程序中: a
with target="_blank"
代替 Axios。 但是,這不適合我,因為它會繞過我的 axios 配置設置(API url、身份驗證令牌等)
似乎您必須根據此示例直接告訴 axios 文件在哪里:
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();
});
我假設您可以簡單地更改 api 上的響應,以使用文件的新 Blob 返回 Blob。 但它似乎需要的主要部分是.then 響應您的 axios 接聽電話。 這樣您仍然可以使用 jwt 驗證用戶的狀態並適當地保護您的文件。
不幸的是,沒有可靠的、跨平台的方法來觸發瀏覽器對正常 web 頁面的下載行為可以滿足這里的要求。 由於您不能在普通 DOM 錨標記上使用帶有內容處置、重定向或數據 URI 的普通 URL,因此我看不到另一種在不創建隱藏a
並單擊它的情況下導致下載的方法。 然而,這似乎運作良好(並且確實是流行的實用程序使用的機制,如filesaver.js )
在 React 中構建一個粗略的DownloadButton
組件來執行此操作非常簡單。 這是一個工作代碼筆,它模擬 Axios 響應,否則從頭到尾工作,除非您想做任何重構。 我正在使用 hooks 和async/await
來保持自己的理智/清晰,但兩者都不是絕對必要的。 它確實在錨標簽上使用了download
屬性,該屬性在現代瀏覽器中都有很好的支持。
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>
);
};
至於 ExpressJS 的響應標頭中未顯示的content-disposition
,我不確定是什么問題。 但是,根據ExpressJS 文檔,第二個參數是文件名,它將作為content-disposition
header 自動發送,因此您不需要在options
參數中自己指定它。 是否顯示其他參數? 如果是這樣,可能在重新定義它時存在沖突options
。 但是,當使用與您的路線類似的路線在本地運行示例時,我都沒有遇到任何問題。
res.download(路徑 [, 文件名] [, 選項] [, fn])
Express v4.16.0 及更高版本支持可選選項參數。
將路徑中的文件作為“附件”傳輸。 通常,瀏覽器會提示用戶下載。 默認情況下,Content-Disposition header “filename=”參數是路徑(這通常出現在瀏覽器對話框中)。 使用文件名參數覆蓋此默認值。
當發生錯誤或傳輸完成時,該方法調用可選的回調 function fn。 此方法使用 res.sendFile() 來傳輸文件。
可選的 options 參數傳遞到底層 res.sendFile() 調用,並采用完全相同的參數。
您必須使用以下命令在 react 中安裝“js-file-download”庫
npm install --save js-file-download
使用 axios 的反應文件中的代碼如下:
import download from 'js-file-download';
downloadFile = () => {
axios.get("localhost:3000/route/path/url")
.then(resp => {
download(resp.data, fileName);
});
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.