8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

如何使用 axios 下载文件

Austin Wolff 2月前

179 0

我正在使用 axios 进行基本的 http 请求,例如 GET 和 POST,效果很好。现在我也需要能够下载 Excel 文件。使用 axios 可以实现吗?如果可以,有人有示例代码吗?

我正在使用 axios 进行基本的 http 请求,例如 GET 和 POST,效果很好。现在我也需要能够下载 Excel 文件。使用 axios 可以实现这一点吗?如果可以,有人有示例代码吗?如果没有,我还可以在 React 应用程序中使用什么来做同样的事情?

帖子版权声明 1、本帖标题:如何使用 axios 下载文件
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Austin Wolff在本站《express》版块原创发布, 转载请注明出处!
最新回复 (0)
  • const express = require(\'express\');const app = express();//从环境文件加载配置require(\'dotenv\').config();const PORT = process.env.PORT || 4000;//要解析的中间件...

    const express = require("express");
    const app = express();
    
    //load config from env file
    require("dotenv").config();
    const PORT = process.env.PORT || 4000;
    
    //middleware to parse json request body
    app.use(express.json());
    
    //import routes for TODO API
    const todoRoutes = require("./routes/todos");
    
    //mount the todo API routes
    app.use("/api/v1", todoRoutes);
    
    
    //start server
    app.listen(PORT, () => {
        console.log(`Server started successfully at ${PORT}`);
    })
    
    //connect to the database
    const dbConnect = require("./config/database");
    dbConnect();
    
    //default Route
    app.get("/", (req,res) => {
        res.send(`<h1> This is HOMEPAGE baby</h1>`);
    })
    
    
    
    **todos.js**`models folder`
    
    const express  = require("express");
    const router = express.Router();
    //import controller
    const {createTodo} = require("../controllers/createTodo");
    
    
    
    //define APi routes
    router.post("/createTodo", createTodo);
    
    
    module.exports = router;
    
    **createTodo.js**`controllers folder`
    //import th model
    const Todo = require("../models/Todo");
    
    //define route handler
    
    exports.createTodo = async(req,res) => {
        try {
                //extract title and description from reauest body
                const {title,description} = req.body;
                //create a new Todo Obj and insert in DB
                const response = await Todo.create({title,description});
                //send a json response with a success flag
                res.status(200).json(
                    {
                        success:true,
                        data:response,
                        message:'Entry Created Successfully'
                    }
                );
        }
        catch(err) {
            console.error(err);
            console.log(err);
            res.status(500)
            .json({
                success:false,
                data:"internal server error",
                message:err.message,
            })
        }
    }
    
    **models**`routes folder`
    
    const mongoose = require("mongoose");
    
    const todoSchema = new mongoose.Schema(
        {
            title:{
                type:String,
                required:true,
                maxLength:50,
            },
            description: {
                type:String,
                required:true,
                maxLength:50,
            },
            createdAt:{
                type:Date,
                required:true,
                default:Date.now(),
            },
            updatedAt:{
                type:Date,
                required:true,
                default:Date.now(),
            }
        }
    );
    
    module.exports = mongoose.model("Todo", todoSchema);
    **
    database.js**`config folder`
    
    const mongoose = require("mongoose");
    
    require("dotenv").config();
    
    const dbConnect = () => {
        mongoose.connect(process.env.DATABASE_URL)
        .then(() => console.log("DB ka Connection is Successful"))
        .catch( (error) => {
            console.log("Issue in DB Connection");
            console.error(error.message);
            process.exit(1);
        } );
    }
    
    

    模块.导出 = 数据库连接;

    这里数据库已连接,但我无法添加数据

    在 postman** 中输入 url 和数据后,它显示了以下错误无法获取 /api/v1/createTodo 错误通常表示我尝试访问的路由未定义在 postman** 中输入 url 和数据后,它显示了以下错误无法获取 /api/v1/createTodo 错误通常表示我尝试访问的路由未定义

    无法获取 /api/v1/createTodo

  • 使用 URL.CreateObject() 的答案对我来说效果很好。我仍然想指出使用 HTTP 标头的选项。

    使用 HttpHeaders 有以下 优点

    • 非常广泛的浏览器支持
    • 不需要在浏览器内存中创建 blob 对象
    • 不需要等待服务器的完整响应即可向用户显示反馈
    • 没有尺寸限制

    使用 HttpHeaders 要求您能够访问从中下载文件的后端服务器 (这似乎是 OP 的 Excel 文件的情况)

    HttpHeaders解决方案:

    前端:

    //... 
    // the download link
    <a href="download/destination?parameter1=foo&param2=bar"> 
    click me to download!
    </a>
    

    后端

    (本例中为 C#,但可以是任何语言。根据需要进行调整)

    ...
    var fs = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Read);
    Response.Headers["Content-Disposition"] = "attachment; filename=someName.txt";
    return File(fs, "application/octet-stream");
    ...
    

    此解决方案假定您可以控制响应的后端服务器。

    https://github.com/eligrey/FileSaver.js/wiki/Saving-a-remote-file#using-http-header

  • 对于axios的POST请求,请求应该是这样的:这里的关键是 responseType header 字段必须在Post的第3个参数中。第2个参数是应用程序参数。

    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;
    }
    
  • 为接收的文档 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 此代码才能工作,如果用户想同时下载多个文件,那么请先使用解决方案,或者如果不行,也尝试其他解决方案

  • 我遇到了一个问题,当我从 axios 下载一个文件 const axiosResponse = await axios.get(pdf.url) 到 google drive 时 googleDrive.files.create({media: {body: axiosResponse.data, mimeType}, requestBody: {name: fileName, parents: [parentFolder], mimeType}, auth: jwtClient}) 上传了一个损坏的文件。

    文件损坏的原因是 axios 将转换 axiosResponse.data 为字符串。为了解决这个问题,我不得不让 axios 返回一个流 axios.get(pdf.url, { responseType: 'stream' }) .

  • 此功能将帮助您下载已准备好的 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)
        }
      };
    
  • Gili 2月前 0 只看Ta
    引用 8

    这对我有用。我在 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 });
    })
    
  • 基本上,我通过从“content-disposition”标头中读取文件名(如果存在)来解决文件名问题:

    const generateFile = async ({ api, url, payload }) => {
        return await api({
            url: url,
            method: 'POST',
            data: payload, // payload
            responseType: 'blob'
        }).catch((e) => {
            throw e;
        });
    };
    
    const getFileName = (fileBlob, defaultFileName) => {
        const contentDisposition = fileBlob.headers.get('content-disposition');
        if (contentDisposition) {
            const fileNameIdentifier = 'filename=';
            const filenamePosition = contentDisposition.indexOf(fileNameIdentifier);
            if (~filenamePosition) {
                return contentDisposition.slice(filenamePosition + fileNameIdentifier.length, contentDisposition.length).replace(/"/g,'');
            }
        }
        return defaultFileName;
    };
    
    const downloadFile = (fileBlob, fileName) => {
        const url = window.URL.createObjectURL(new Blob([fileBlob]));
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', `${fileName}`);
        document.body.appendChild(link);
        link.click();
        link.remove();
        link.style.display = 'none';
        window.URL.revokeObjectURL(url);
    };
    
    // "api" is an instance of Axios (axios.create)
    // "payload" is the payload you submit to the server
    const fileBlob = await generateFile({ api, '/url/to/download', payload });
    const fileName = getFileName(fileBlob, "MyDownload.xls");
    downloadFile(fileBlob.data, fileName);
    
  • 对于那些想要实现经过验证的本机下载的人。

    我目前正在使用 Axios 开发 SPA。
    在这种情况下 stream 不允许

    // `responseType` indicates the type of data that the server will respond with
    // options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
    // browser only: 'blob'
    

    但我找到了 本主题中 .
    诀窍是发送一个包含你的令牌和目标文件的基本表单 POST。

    \'这将定位到一个新窗口。一旦浏览器读取了服务器响应中的附件标头,它就会关闭新选项卡并开始下载。\'

    以下是一个示例:

    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);
    

    \'您可能需要将您的处理程序标记为未经身份验证/匿名,以便您可以手动验证 JWT 以确保正确授权。\'

    我的 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
    }
    
  • 您需要从后端返回文件({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));
        }
    
  • 使用自定义标头请求下载文件。此示例展示了如何使用 bearer token 发送文件下载请求。适用于需要授权的可下载内容。

        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();
    });
      }
  • 诀窍是在中创建一个不可见的锚标签, 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 。你可以在这里找到类似的想法: https://thewebtier.com/snippets/download-files-with-axios/ .

  • 大多数答案都缺少几个关键点。

    我将尝试在这里进行深入解释。

    太长了;

    如果你正在创建 a 标签链接并通过浏览器请求启动下载,那么

    1. p4

    2. p5


    有关组件代码和更深入的分析,请进一步阅读

    首先要弄清楚您尝试下载数据的 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>;
    }
    
    

    很高兴知道:

    1. p18

    2. 第19页

    3. p20

    4. p21

    5. 第22页

    6. p23

    参考: AJAX 请求和常规浏览器请求之间的区别

  •         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.
    })
    
  • 在我看来,这提供了最佳的用户体验。在 cookie 中设置授权很麻烦,但值得。客户端需要 axios.defaults.withCredentials = true。服务器端是完成繁重工作的地方。

  • 如果您控制服务器,那么您可以简单地将访问令牌存储为 cookie,浏览器会将其添加到对您服务器的任何请求中。medium.com/@ryanchenkie_40935/...

  • 看起来,postman 发送的 http 方法是 GET。在路由器模块 todos.js 中,只处理了 POST 方法。这可能是问题所在。请在发送数据之前在 postman 中选择 POST 方法。

  • 这是触发用户下载的非常简单的 JavaScript 代码:

    window.open("<insert URL here>")
    

    您不需要/不需要 axios 来执行此操作;让浏览器执行其操作应该是标准做法。

    注意: 如果您需要授权才能下载,那么这可能不起作用。我很确定您可以使用 cookies 来授权这样的请求,前提是它在同一个域内,但无论如何,在这种情况下这可能不会立即起作用。


    至于是否可能 不是... ... 使用内置文件下载机制,不 .

  • \“对象文字可能仅指定已知属性,但类型‘BlobPropertyBag’中不存在‘encoding’。您是不是想写‘endings’?”\“

返回
作者最近主题: