如何使用axios下载文件
How to download files using axios
我将 axios 用于基本的 http 请求,例如 GET 和 POST,并且效果很好。现在我还需要能够下载 Excel 个文件。 axios 可以吗?如果是这样,有人有一些示例代码吗?如果没有,我还可以在 React 应用程序中使用什么来做同样的事情?
当响应带有可下载文件时,响应 headers 将类似于
Content-Disposition: "attachment;filename=report.xls"
Content-Type: "application/octet-stream" // or Content-type: "application/vnd.ms-excel"
您可以做的是创建一个单独的组件,其中将包含一个隐藏的 iframe。
import * as React from 'react';
var MyIframe = React.createClass({
render: function() {
return (
<div style={{display: 'none'}}>
<iframe src={this.props.iframeSrc} />
</div>
);
}
});
现在,您可以将可下载文件的 url 作为 prop 传递给此组件,因此当此组件收到 prop 时,它会 re-render 并下载文件。
编辑:您也可以使用js-file-download module. Link to Github repo
const FileDownload = require('js-file-download');
Axios({
url: 'http://localhost/downloadFile',
method: 'GET',
responseType: 'blob', // Important
}).then((response) => {
FileDownload(response.data, 'report.csv');
});
希望这对您有所帮助:)
下载文件(使用 Axios 和安全)
当您想使用 Axios 和一些安全手段下载文件时,这实际上更加复杂。为了防止其他人花太多时间来解决这个问题,让我来向您介绍一下。
你需要做三件事:
- 配置您的服务器以允许浏览器查看所需的 HTTP headers
- 实施 server-side 服务,并使其公布下载文件的正确文件类型。
- 实施 Axios 处理程序以在浏览器中触发 FileDownload 对话框
这些步骤大部分是可行的 - 但由于浏览器与 CORS 的关系而变得相当复杂。一步一步:
1。配置您的 (HTTP) 服务器
采用传输安全性时,JavaScript 在浏览器中执行可以[按设计] 仅访问 HTTP 服务器实际发送的 HTTP header 中的 6 个。如果我们希望服务器为下载建议一个文件名,我们必须通知浏览器“可以”JavaScript 被授予访问其他 header 的权限,建议的文件名将被传输.
为了便于讨论,我们假设我们希望服务器在名为 X-Suggested-Filename 的 HTTP header 中传输建议的文件名。 HTTP 服务器告诉浏览器可以 OK 将收到的自定义 header 公开给 JavaScript/Axios 并使用以下 header:
Access-Control-Expose-Headers: X-Suggested-Filename
配置 HTTP 服务器以设置此项的确切方法 header 因产品而异。
See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers for a full explanation and detailed description of these standard headers.
2。实施 server-side 服务
您的 server-side 服务实现现在必须执行两件事:
- 创建(二进制)文档并将正确的 ContentType 分配给响应
- 为客户端分配包含建议文件名的自定义 header (X-Suggested-Filename)
根据您选择的技术堆栈,这可以通过不同的方式完成。我将使用 JavaEE 7 标准绘制一个示例,它应该发出一个 Excel 报告:
@GET
@Path("/report/excel")
@Produces("application/vnd.ms-excel")
public Response getAllergyAndPreferencesReport() {
// Create the document which should be downloaded
final byte[] theDocumentData = ....
// Define a suggested filename
final String filename = ...
// Create the JAXRS response
// Don't forget to include the filename in 2 HTTP headers:
//
// a) The standard 'Content-Disposition' one, and
// b) The custom 'X-Suggested-Filename'
//
final Response.ResponseBuilder builder = Response.ok(
theDocumentData, "application/vnd.ms-excel")
.header("X-Suggested-Filename", fileName);
builder.header("Content-Disposition", "attachment; filename=" + fileName);
// All Done.
return builder.build();
}
该服务现在发出二进制文档(在本例中为 Excel 报告),设置正确的内容类型 - 并发送包含建议文件名的自定义 HTTP header正在保存文档。
3。为收到的文档实施 Axios 处理程序
这里有一些陷阱,所以让我们确保所有细节都正确配置:
- 服务响应@GET(即HTTP GET),所以axios调用必须是'axios.get(...)'.
- 文档以字节流的形式传输,因此您必须告诉 Axios 将响应视为 HTML5 Blob。 (即 responseType:'blob')。
- 在这种情况下,file-saver JavaScript 库用于弹出浏览器对话框。但是,您可以选择另一个。
Axios 实现的框架将类似于:
// Fetch the dynamically generated excel document from the server.
axios.get(resource, {responseType: 'blob'}).then((response) => {
// Log somewhat to show that the browser actually exposes the custom HTTP header
const fileNameHeader = "x-suggested-filename";
const suggestedFileName = response.headers[fileNameHeader];
const effectiveFileName = (suggestedFileName === undefined
? "allergierOchPreferenser.xls"
: suggestedFileName);
console.log(`Received header [${fileNameHeader}]: ${suggestedFileName}, effective fileName: ${effectiveFileName}`);
// Let the user save the file.
FileSaver.saveAs(response.data, effectiveFileName);
}).catch((response) => {
console.error("Could not Download the Excel report from the backend.", response);
});
我的回答是 完全破解- 我刚刚创建了一个看起来像按钮的 link 并向其添加了 URL。
<a class="el-button"
style="color: white; background-color: #58B7FF;"
:href="<YOUR URL ENDPOINT HERE>"
:download="<FILE NAME NERE>">
<i class="fa fa-file-excel-o"></i> Excel
</a>
我使用的是优秀的 VueJs,因此使用了奇怪的注释,但是,此解决方案与框架无关。这个想法适用于任何基于 HTML 的设计。
诀窍是在 render()
中创建一个不可见的锚标记,并添加一个 React ref
允许在我们获得 axios 响应后触发点击:
class Example extends Component {
state = {
ref: React.createRef()
}
exportCSV = () => {
axios.get(
'/app/export'
).then(response => {
let blob = new Blob([response.data], {type: 'application/octet-stream'})
let ref = this.state.ref
ref.current.href = URL.createObjectURL(blob)
ref.current.download = 'data.csv'
ref.current.click()
})
}
render(){
return(
<div>
<a style={{display: 'none'}} href='empty' ref={this.state.ref}>ref</a>
<button onClick={this.exportCSV}>Export CSV</button>
</div>
)
}
}
这是文档:https://reactjs.org/docs/refs-and-the-dom.html. You can find a similar idea here: https://thewebtier.com/snippets/download-files-with-axios/。
更通用的解决方案
axios({
url: 'http://api.dev/file-download', //your url
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'); //or any other extension
document.body.appendChild(link);
link.click();
});
在 https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743
查看怪癖
axios.get(
'/app/export'
).then(response => {
const url = window.URL.createObjectURL(new Blob([response]));
const link = document.createElement('a');
link.href = url;
const fileName = `${+ new Date()}.csv`// whatever your file name .
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
link.remove();// you need to remove that elelment which is created before.
})
Axios.post IE等浏览器解决方案
我在这里找到了一些令人难以置信的解决方案。但他们经常不考虑 IE 浏览器的问题。也许这会为其他人节省一些时间。
axios.post("/yourUrl",
data,
{ responseType: 'blob' }
).then(function (response) {
let fileName = response.headers["content-disposition"].split("filename=")[1];
if (window.navigator && window.navigator.msSaveOrOpenBlob) { // IE variant
window.navigator.msSaveOrOpenBlob(new Blob([response.data],
{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
),
fileName
);
} else {
const url = window.URL.createObjectURL(new Blob([response.data],
{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download',
response.headers["content-disposition"].split("filename=")[1]);
document.body.appendChild(link);
link.click();
}
}
);
以上示例适用于 excel 个文件,但只要稍加改动即可应用于任何格式。
我在服务器上这样做是为了发送 excel 文件。
response.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=exceptions.xlsx")
触发用户下载的代码非常简单javascript:
window.open("<insert URL here>")
您没有 want/need axios 进行此操作;让浏览器做这件事应该是标准的。
注意:如果您需要授权才能下载,那么这可能不起作用。我很确定您可以使用 cookie 来授权这样的请求,前提是它在同一个域中,但无论如何,在这种情况下这可能不会立即起作用。
至于有没有可能...not with the in-built file downloading mechanism, no.
The function to make the API call with axios:
function getFileToDownload (apiUrl) {
return axios.get(apiUrl, {
responseType: 'arraybuffer',
headers: {
'Content-Type': 'application/json'
}
})
}
Call the function and then download the excel file you get:
getFileToDownload('putApiUrlHere')
.then (response => {
const type = response.headers['content-type']
const blob = new Blob([response.data], { type: type, encoding: 'UTF-8' })
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'file.xlsx'
link.click()
})
对于axios POST请求,请求应该是这样的:
这里的关键是 responseType
和 header
字段必须在 Post 的第 3 个参数中。第二个参数是应用参数。
export const requestDownloadReport = (requestParams) => async dispatch => {
let response = null;
try {
response = await frontEndApi.post('createPdf', {
requestParams: requestParams,
},
{
responseType: 'arraybuffer', // important...because we need to convert it to a blob. If we don't specify this, response.data will be the raw data. It cannot be converted to blob directly.
headers: {
'Content-Type': 'application/json',
'Accept': 'application/pdf'
}
});
}
catch(err) {
console.log('[requestDownloadReport][ERROR]', err);
return err
}
return response;
}
这对我有用。我在 reactJS
中实现了这个解决方案
const requestOptions = {`enter code here`
method: 'GET',
headers: { 'Content-Type': 'application/json' }
};
fetch(`${url}`, requestOptions)
.then((res) => {
return res.blob();
})
.then((blob) => {
const href = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = href;
link.setAttribute('download', 'config.json'); //or any other extension
document.body.appendChild(link);
link.click();
})
.catch((err) => {
return Promise.reject({ Error: 'Something Went Wrong', err });
})
为接收到的文档实现一个Axios
处理程序,数据格式octect-stream
,
数据可能看起来很奇怪 PK something JbxfFGvddvbdfbVVH34365436fdkln
因为它的八位字节流格式,您最终可能会使用此数据创建文件可能已损坏,{responseType: 'blob'}
将使数据成为可读格式,
axios.get("URL", {responseType: 'blob'})
.then((r) => {
let fileName = r.headers['content-disposition'].split('filename=')[1];
let blob = new Blob([r.data]);
window.saveAs(blob, fileName);
}).catch(err => {
console.log(err);
});
您可能已经尝试过像这样失败的解决方案,
window.saveAs(blob, 'file.zip')
将尝试将文件另存为 zip 但不起作用,
const downloadFile = (fileData) => {
axios.get(baseUrl+"/file/download/"+fileData.id)
.then((response) => {
console.log(response.data);
const blob = new Blob([response.data], {type: response.headers['content-type'], encoding:'UTF-8'});
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'file.zip';
link.click();
})
.catch((err) => console.log(err))
}
const downloadFile = (fileData) => {
axios.get(baseUrl+"/file/download/"+fileData.id)
.then((response) => {
console.log(response);
//const binaryString = window.atob(response.data)
//const bytes = new Uint8Array(response.data)
//const arrBuff = bytes.map((byte, i) => response.data.charCodeAt(i));
//var base64 = btoa(String.fromCharCode.apply(null, new Uint8Array(response.data)));
const blob = new Blob([response.data], {type:"application/octet-stream"});
window.saveAs(blob, 'file.zip')
// const link = document.createElement('a');
// link.href = window.URL.createObjectURL(blob);
// link.download = 'file.zip';
// link.click();
})
.catch((err) => console.log(err))
}
function base64ToArrayBuffer(base64) {
var binaryString = window.atob(base64);
var binaryLen = binaryString.length;
var bytes = new Uint8Array(binaryLen);
for (var i = 0; i < binaryLen; i++) {
var ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
};
return bytes;
}
另一个简短的解决方案是,
window.open("URL")
将继续不必要地打开新选项卡,用户可能必须 allow popups
才能工作此代码,如果用户想同时下载多个文件怎么办,请先使用解决方案,否则请尝试其他解决方案解决方案也
你需要 return File({file_to_download}, "application/vnd.ms-excel") 从你的后端到前端和你的js文件您需要更新下面编写的代码:
function exportToExcel() {
axios.post({path to call your controller}, null,
{
headers:
{
'Content-Disposition': "attachment; filename=XYZ.xlsx",
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
},
responseType: 'arraybuffer',
}
).then((r) => {
const path= window.URL.createObjectURL(new Blob([r.data]));
const link = document.createElement('a');
link.href = path;
link.setAttribute('download', 'XYZ.xlsx');
document.body.appendChild(link);
link.click();
}).catch((error) => console.log(error));
}
此功能将帮助您下载准备好的 xlsx、csv 等文件下载。我只是从后端发送了一个准备好的 xlsx 静态文件,它在反应中。
const downloadFabricFormat = async () => {
try{
await axios({
url: '/api/fabric/fabric_excel_format/',
method: 'GET',
responseType: 'blob',
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'Fabric Excel Format.xlsx');
document.body.appendChild(link);
link.click();
});
} catch(error){
console.log(error)
}
};
大多数答案都缺少几个关键点。
我会尝试在这里进行更深入的解释。
TLDR;
如果您要创建 a
标签 link 并通过浏览器请求启动下载,则
总是叫window.URL.revokeObjectURL(url);
。否则可以
不必要的内存峰值。
无需使用 document.body.appendChild(link);
将创建的 link 附加到文档 body,避免不必要地删除 child稍后。
有关组件代码和更深入的分析,请进一步阅读
首先要确定您尝试从中下载数据的 API 端点是 public 还是私有端点。您是否可以控制服务器?
如果服务器响应
Content-Disposition: attachment; filename=dummy.pdf
Content-Type: application/pdf
浏览器将始终尝试下载名称为 'dummy.pdf'
的文件
如果服务器响应
Content-Disposition: inline; filename=dummy.pdf
Content-Type: application/pdf
浏览器将首先尝试打开本地文件 reader(如果可用,名称为 'dummy.pdf'),否则它将开始文件下载。
如果服务器响应 以上 2 headers
如果未设置下载属性,浏览器(至少 chrome)将尝试打开文件。如果设置,它将下载文件。在 url 不是 blob 的情况下,文件名将是最后一个路径参数的值。
除此之外,请记住从服务器使用 Transfer-Encoding: chunked
从服务器传输大量数据。这将确保客户端知道在没有 Content-Length
header
的情况下何时停止读取当前请求
私人文件
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>;
}
对于 Public 个文件
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
方法实现进度条
来自服务器的分块响应不(不能)指示 Content-Length。因此,如果您在构建进度条时使用它们,则需要通过某种方式了解响应大小。
<a>
标签 links 只能发出 GET HTTP 请求而没有任何能力发送 headers 或
服务器的 cookie(非常适合从 public 个端点下载)
浏览器请求与代码中的 XHR 请求略有不同。
参考:Difference between AJAX request and a regular browser request
使用自定义 header 请求下载文件。在此示例中,它显示了如何使用不记名令牌发送文件下载请求。适用于需要授权的可下载内容。
download(urlHere) {
axios.get(urlHere, {
headers: {
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${sessionStorage.getItem("auth-token")}`,
}
}).then((response) => {
const temp = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = temp;
link.setAttribute('download', 'file.csv'); //or any other extension
document.body.appendChild(link);
link.click();
});
}
对于那些想要实施经过身份验证的本机下载的人。
我目前正在使用 Axios 开发 SPA。
不幸的是,在这种情况下,Axios 不允许 stream
响应类型。
来自文档:
// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// browser only: 'blob'
但我想出了一个解决方法 in this topic。
诀窍是发送包含您的令牌和目标文件的基本表单 POST。
"That targets a new window. Once the browser reads the attachment header on the server response, it will close the new tab and begin the download."
这是一个示例:
let form = document.createElement('form');
form.method = 'post';
form.target = '_blank';
form.action = `${API_URL}/${targetedResource}`;
form.innerHTML = `'<input type="hidden" name="jwtToken" value="${jwtToken}">'`;
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
"You may need to mark your handler as unauthenticated/anonymous so that you can manually validate the JWT to ensure proper authorization."
我的 ASP.NET 实施结果:
[AllowAnonymous]
[HttpPost("{targetedResource}")]
public async Task<IActionResult> GetFile(string targetedResource, [FromForm] string jwtToken)
{
var jsonWebTokenHandler = new JsonWebTokenHandler();
var validationParameters = new TokenValidationParameters()
{
// Your token validation parameters here
};
var tokenValidationResult = jsonWebTokenHandler.ValidateToken(jwtToken, validationParameters);
if (!tokenValidationResult.IsValid)
{
return Unauthorized();
}
// Your file upload implementation here
}
我将 axios 用于基本的 http 请求,例如 GET 和 POST,并且效果很好。现在我还需要能够下载 Excel 个文件。 axios 可以吗?如果是这样,有人有一些示例代码吗?如果没有,我还可以在 React 应用程序中使用什么来做同样的事情?
当响应带有可下载文件时,响应 headers 将类似于
Content-Disposition: "attachment;filename=report.xls"
Content-Type: "application/octet-stream" // or Content-type: "application/vnd.ms-excel"
您可以做的是创建一个单独的组件,其中将包含一个隐藏的 iframe。
import * as React from 'react';
var MyIframe = React.createClass({
render: function() {
return (
<div style={{display: 'none'}}>
<iframe src={this.props.iframeSrc} />
</div>
);
}
});
现在,您可以将可下载文件的 url 作为 prop 传递给此组件,因此当此组件收到 prop 时,它会 re-render 并下载文件。
编辑:您也可以使用js-file-download module. Link to Github repo
const FileDownload = require('js-file-download');
Axios({
url: 'http://localhost/downloadFile',
method: 'GET',
responseType: 'blob', // Important
}).then((response) => {
FileDownload(response.data, 'report.csv');
});
希望这对您有所帮助:)
下载文件(使用 Axios 和安全)
当您想使用 Axios 和一些安全手段下载文件时,这实际上更加复杂。为了防止其他人花太多时间来解决这个问题,让我来向您介绍一下。
你需要做三件事:
- 配置您的服务器以允许浏览器查看所需的 HTTP headers
- 实施 server-side 服务,并使其公布下载文件的正确文件类型。
- 实施 Axios 处理程序以在浏览器中触发 FileDownload 对话框
这些步骤大部分是可行的 - 但由于浏览器与 CORS 的关系而变得相当复杂。一步一步:
1。配置您的 (HTTP) 服务器
采用传输安全性时,JavaScript 在浏览器中执行可以[按设计] 仅访问 HTTP 服务器实际发送的 HTTP header 中的 6 个。如果我们希望服务器为下载建议一个文件名,我们必须通知浏览器“可以”JavaScript 被授予访问其他 header 的权限,建议的文件名将被传输.
为了便于讨论,我们假设我们希望服务器在名为 X-Suggested-Filename 的 HTTP header 中传输建议的文件名。 HTTP 服务器告诉浏览器可以 OK 将收到的自定义 header 公开给 JavaScript/Axios 并使用以下 header:
Access-Control-Expose-Headers: X-Suggested-Filename
配置 HTTP 服务器以设置此项的确切方法 header 因产品而异。
See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers for a full explanation and detailed description of these standard headers.
2。实施 server-side 服务
您的 server-side 服务实现现在必须执行两件事:
- 创建(二进制)文档并将正确的 ContentType 分配给响应
- 为客户端分配包含建议文件名的自定义 header (X-Suggested-Filename)
根据您选择的技术堆栈,这可以通过不同的方式完成。我将使用 JavaEE 7 标准绘制一个示例,它应该发出一个 Excel 报告:
@GET
@Path("/report/excel")
@Produces("application/vnd.ms-excel")
public Response getAllergyAndPreferencesReport() {
// Create the document which should be downloaded
final byte[] theDocumentData = ....
// Define a suggested filename
final String filename = ...
// Create the JAXRS response
// Don't forget to include the filename in 2 HTTP headers:
//
// a) The standard 'Content-Disposition' one, and
// b) The custom 'X-Suggested-Filename'
//
final Response.ResponseBuilder builder = Response.ok(
theDocumentData, "application/vnd.ms-excel")
.header("X-Suggested-Filename", fileName);
builder.header("Content-Disposition", "attachment; filename=" + fileName);
// All Done.
return builder.build();
}
该服务现在发出二进制文档(在本例中为 Excel 报告),设置正确的内容类型 - 并发送包含建议文件名的自定义 HTTP header正在保存文档。
3。为收到的文档实施 Axios 处理程序
这里有一些陷阱,所以让我们确保所有细节都正确配置:
- 服务响应@GET(即HTTP GET),所以axios调用必须是'axios.get(...)'.
- 文档以字节流的形式传输,因此您必须告诉 Axios 将响应视为 HTML5 Blob。 (即 responseType:'blob')。
- 在这种情况下,file-saver JavaScript 库用于弹出浏览器对话框。但是,您可以选择另一个。
Axios 实现的框架将类似于:
// Fetch the dynamically generated excel document from the server.
axios.get(resource, {responseType: 'blob'}).then((response) => {
// Log somewhat to show that the browser actually exposes the custom HTTP header
const fileNameHeader = "x-suggested-filename";
const suggestedFileName = response.headers[fileNameHeader];
const effectiveFileName = (suggestedFileName === undefined
? "allergierOchPreferenser.xls"
: suggestedFileName);
console.log(`Received header [${fileNameHeader}]: ${suggestedFileName}, effective fileName: ${effectiveFileName}`);
// Let the user save the file.
FileSaver.saveAs(response.data, effectiveFileName);
}).catch((response) => {
console.error("Could not Download the Excel report from the backend.", response);
});
我的回答是 完全破解- 我刚刚创建了一个看起来像按钮的 link 并向其添加了 URL。
<a class="el-button"
style="color: white; background-color: #58B7FF;"
:href="<YOUR URL ENDPOINT HERE>"
:download="<FILE NAME NERE>">
<i class="fa fa-file-excel-o"></i> Excel
</a>
我使用的是优秀的 VueJs,因此使用了奇怪的注释,但是,此解决方案与框架无关。这个想法适用于任何基于 HTML 的设计。
诀窍是在 render()
中创建一个不可见的锚标记,并添加一个 React ref
允许在我们获得 axios 响应后触发点击:
class Example extends Component {
state = {
ref: React.createRef()
}
exportCSV = () => {
axios.get(
'/app/export'
).then(response => {
let blob = new Blob([response.data], {type: 'application/octet-stream'})
let ref = this.state.ref
ref.current.href = URL.createObjectURL(blob)
ref.current.download = 'data.csv'
ref.current.click()
})
}
render(){
return(
<div>
<a style={{display: 'none'}} href='empty' ref={this.state.ref}>ref</a>
<button onClick={this.exportCSV}>Export CSV</button>
</div>
)
}
}
这是文档:https://reactjs.org/docs/refs-and-the-dom.html. You can find a similar idea here: https://thewebtier.com/snippets/download-files-with-axios/。
更通用的解决方案
axios({
url: 'http://api.dev/file-download', //your url
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'); //or any other extension
document.body.appendChild(link);
link.click();
});
在 https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743
查看怪癖 axios.get(
'/app/export'
).then(response => {
const url = window.URL.createObjectURL(new Blob([response]));
const link = document.createElement('a');
link.href = url;
const fileName = `${+ new Date()}.csv`// whatever your file name .
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
link.remove();// you need to remove that elelment which is created before.
})
Axios.post IE等浏览器解决方案
我在这里找到了一些令人难以置信的解决方案。但他们经常不考虑 IE 浏览器的问题。也许这会为其他人节省一些时间。
axios.post("/yourUrl",
data,
{ responseType: 'blob' }
).then(function (response) {
let fileName = response.headers["content-disposition"].split("filename=")[1];
if (window.navigator && window.navigator.msSaveOrOpenBlob) { // IE variant
window.navigator.msSaveOrOpenBlob(new Blob([response.data],
{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
),
fileName
);
} else {
const url = window.URL.createObjectURL(new Blob([response.data],
{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download',
response.headers["content-disposition"].split("filename=")[1]);
document.body.appendChild(link);
link.click();
}
}
);
以上示例适用于 excel 个文件,但只要稍加改动即可应用于任何格式。
我在服务器上这样做是为了发送 excel 文件。
response.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=exceptions.xlsx")
触发用户下载的代码非常简单javascript:
window.open("<insert URL here>")
您没有 want/need axios 进行此操作;让浏览器做这件事应该是标准的。
注意:如果您需要授权才能下载,那么这可能不起作用。我很确定您可以使用 cookie 来授权这样的请求,前提是它在同一个域中,但无论如何,在这种情况下这可能不会立即起作用。
至于有没有可能...not with the in-built file downloading mechanism, no.
The function to make the API call with axios:
function getFileToDownload (apiUrl) {
return axios.get(apiUrl, {
responseType: 'arraybuffer',
headers: {
'Content-Type': 'application/json'
}
})
}
Call the function and then download the excel file you get:
getFileToDownload('putApiUrlHere')
.then (response => {
const type = response.headers['content-type']
const blob = new Blob([response.data], { type: type, encoding: 'UTF-8' })
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'file.xlsx'
link.click()
})
对于axios POST请求,请求应该是这样的:
这里的关键是 responseType
和 header
字段必须在 Post 的第 3 个参数中。第二个参数是应用参数。
export const requestDownloadReport = (requestParams) => async dispatch => {
let response = null;
try {
response = await frontEndApi.post('createPdf', {
requestParams: requestParams,
},
{
responseType: 'arraybuffer', // important...because we need to convert it to a blob. If we don't specify this, response.data will be the raw data. It cannot be converted to blob directly.
headers: {
'Content-Type': 'application/json',
'Accept': 'application/pdf'
}
});
}
catch(err) {
console.log('[requestDownloadReport][ERROR]', err);
return err
}
return response;
}
这对我有用。我在 reactJS
中实现了这个解决方案const requestOptions = {`enter code here`
method: 'GET',
headers: { 'Content-Type': 'application/json' }
};
fetch(`${url}`, requestOptions)
.then((res) => {
return res.blob();
})
.then((blob) => {
const href = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = href;
link.setAttribute('download', 'config.json'); //or any other extension
document.body.appendChild(link);
link.click();
})
.catch((err) => {
return Promise.reject({ Error: 'Something Went Wrong', err });
})
为接收到的文档实现一个Axios
处理程序,数据格式octect-stream
,
数据可能看起来很奇怪 PK something JbxfFGvddvbdfbVVH34365436fdkln
因为它的八位字节流格式,您最终可能会使用此数据创建文件可能已损坏,{responseType: 'blob'}
将使数据成为可读格式,
axios.get("URL", {responseType: 'blob'})
.then((r) => {
let fileName = r.headers['content-disposition'].split('filename=')[1];
let blob = new Blob([r.data]);
window.saveAs(blob, fileName);
}).catch(err => {
console.log(err);
});
您可能已经尝试过像这样失败的解决方案,
window.saveAs(blob, 'file.zip')
将尝试将文件另存为 zip 但不起作用,
const downloadFile = (fileData) => {
axios.get(baseUrl+"/file/download/"+fileData.id)
.then((response) => {
console.log(response.data);
const blob = new Blob([response.data], {type: response.headers['content-type'], encoding:'UTF-8'});
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'file.zip';
link.click();
})
.catch((err) => console.log(err))
}
const downloadFile = (fileData) => {
axios.get(baseUrl+"/file/download/"+fileData.id)
.then((response) => {
console.log(response);
//const binaryString = window.atob(response.data)
//const bytes = new Uint8Array(response.data)
//const arrBuff = bytes.map((byte, i) => response.data.charCodeAt(i));
//var base64 = btoa(String.fromCharCode.apply(null, new Uint8Array(response.data)));
const blob = new Blob([response.data], {type:"application/octet-stream"});
window.saveAs(blob, 'file.zip')
// const link = document.createElement('a');
// link.href = window.URL.createObjectURL(blob);
// link.download = 'file.zip';
// link.click();
})
.catch((err) => console.log(err))
}
function base64ToArrayBuffer(base64) {
var binaryString = window.atob(base64);
var binaryLen = binaryString.length;
var bytes = new Uint8Array(binaryLen);
for (var i = 0; i < binaryLen; i++) {
var ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
};
return bytes;
}
另一个简短的解决方案是,
window.open("URL")
将继续不必要地打开新选项卡,用户可能必须 allow popups
才能工作此代码,如果用户想同时下载多个文件怎么办,请先使用解决方案,否则请尝试其他解决方案解决方案也
你需要 return File({file_to_download}, "application/vnd.ms-excel") 从你的后端到前端和你的js文件您需要更新下面编写的代码:
function exportToExcel() {
axios.post({path to call your controller}, null,
{
headers:
{
'Content-Disposition': "attachment; filename=XYZ.xlsx",
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
},
responseType: 'arraybuffer',
}
).then((r) => {
const path= window.URL.createObjectURL(new Blob([r.data]));
const link = document.createElement('a');
link.href = path;
link.setAttribute('download', 'XYZ.xlsx');
document.body.appendChild(link);
link.click();
}).catch((error) => console.log(error));
}
此功能将帮助您下载准备好的 xlsx、csv 等文件下载。我只是从后端发送了一个准备好的 xlsx 静态文件,它在反应中。
const downloadFabricFormat = async () => {
try{
await axios({
url: '/api/fabric/fabric_excel_format/',
method: 'GET',
responseType: 'blob',
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'Fabric Excel Format.xlsx');
document.body.appendChild(link);
link.click();
});
} catch(error){
console.log(error)
}
};
大多数答案都缺少几个关键点。
我会尝试在这里进行更深入的解释。
TLDR;
如果您要创建 a
标签 link 并通过浏览器请求启动下载,则
总是叫
window.URL.revokeObjectURL(url);
。否则可以 不必要的内存峰值。无需使用
document.body.appendChild(link);
将创建的 link 附加到文档 body,避免不必要地删除 child稍后。
有关组件代码和更深入的分析,请进一步阅读
首先要确定您尝试从中下载数据的 API 端点是 public 还是私有端点。您是否可以控制服务器?
如果服务器响应
Content-Disposition: attachment; filename=dummy.pdf
Content-Type: application/pdf
浏览器将始终尝试下载名称为 'dummy.pdf'
的文件如果服务器响应
Content-Disposition: inline; filename=dummy.pdf
Content-Type: application/pdf
浏览器将首先尝试打开本地文件 reader(如果可用,名称为 'dummy.pdf'),否则它将开始文件下载。
如果服务器响应 以上 2 headers
如果未设置下载属性,浏览器(至少 chrome)将尝试打开文件。如果设置,它将下载文件。在 url 不是 blob 的情况下,文件名将是最后一个路径参数的值。
除此之外,请记住从服务器使用 Transfer-Encoding: chunked
从服务器传输大量数据。这将确保客户端知道在没有 Content-Length
header
私人文件
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>;
}
对于 Public 个文件
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
方法实现进度条来自服务器的分块响应不(不能)指示 Content-Length。因此,如果您在构建进度条时使用它们,则需要通过某种方式了解响应大小。
<a>
标签 links 只能发出 GET HTTP 请求而没有任何能力发送 headers 或 服务器的 cookie(非常适合从 public 个端点下载)浏览器请求与代码中的 XHR 请求略有不同。
参考:Difference between AJAX request and a regular browser request
使用自定义 header 请求下载文件。在此示例中,它显示了如何使用不记名令牌发送文件下载请求。适用于需要授权的可下载内容。
download(urlHere) {
axios.get(urlHere, {
headers: {
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${sessionStorage.getItem("auth-token")}`,
}
}).then((response) => {
const temp = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = temp;
link.setAttribute('download', 'file.csv'); //or any other extension
document.body.appendChild(link);
link.click();
});
}
对于那些想要实施经过身份验证的本机下载的人。
我目前正在使用 Axios 开发 SPA。
不幸的是,在这种情况下,Axios 不允许 stream
响应类型。
来自文档:
// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// browser only: 'blob'
但我想出了一个解决方法 in this topic。
诀窍是发送包含您的令牌和目标文件的基本表单 POST。
"That targets a new window. Once the browser reads the attachment header on the server response, it will close the new tab and begin the download."
这是一个示例:
let form = document.createElement('form');
form.method = 'post';
form.target = '_blank';
form.action = `${API_URL}/${targetedResource}`;
form.innerHTML = `'<input type="hidden" name="jwtToken" value="${jwtToken}">'`;
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
"You may need to mark your handler as unauthenticated/anonymous so that you can manually validate the JWT to ensure proper authorization."
我的 ASP.NET 实施结果:
[AllowAnonymous]
[HttpPost("{targetedResource}")]
public async Task<IActionResult> GetFile(string targetedResource, [FromForm] string jwtToken)
{
var jsonWebTokenHandler = new JsonWebTokenHandler();
var validationParameters = new TokenValidationParameters()
{
// Your token validation parameters here
};
var tokenValidationResult = jsonWebTokenHandler.ValidateToken(jwtToken, validationParameters);
if (!tokenValidationResult.IsValid)
{
return Unauthorized();
}
// Your file upload implementation here
}