使用axios如何下载文件

指针潮涌
• 阅读 33396

先上结论,再谈细节:

  1. 前端 axios 的 config 配置 responseType: 'blob' (关键)
  2. 服务端读取文件,以 content-type: 'application/octet-stream' 的格式返回前端
  3. 前端接收到数据后,使用 Blob 来接收数据,并且创建a标签进行下载

前端发起请求

在使用 axios 发起请求前,首先了解一下 responseType 这个属性是如何在 axios 中使用的。以 axios 最常用的 getpost 请求为例,这两种请求方式在传递参数的时候也是不同的。使用的版本 axios@0.19.2

  • 如何传递 responseType?

使用axios如何下载文件

文件:lib/core/Axios.js
结论:可以看到对于 get 和 post 请求,axios的处理方式是不同的。在get请求当中,config 是第二个参数,而到了 post 里 config 变成了第三个参数,这是传递 responseType 第一个需要注意的地方。

  • 为什么要传递 responseType?

使用axios如何下载文件

文件:lib/adapters/xhr.js
结论:默认情况下,config.responseType 是 undefined,所以通常情况下,我们得到的 response 中的 data 一定是 request.responseText 这个 string 对象。但是对于文件下载,我们需要使用的是后面的 request.response 这个对象,这就是为什么要传递 responseType 的原因

  • responseType 为什么是 'blob'?

使用axios如何下载文件

来源:mdn 中对于 xhr.response 属性的介绍
结论:可以看到,response 返回的数据格式由 responseType 来决定,因为我们需要在回调里使用 Blob 构造函数来接收数据,所以我们传递的 reponseType 也应该是 'blob',保证数据格式的统一

// 通过以上几点那么针对axios设置reponseType的方式应该类似下面
const url = '/info/download'
// get、delete、head 等请求
axios.get(url, {params: {...someParmas}, responseType: 'blob'})
    .then((res) => {
        //...
    }).catch((err) => {
        //
    })

// post、put、patch 等请求
axios.post(url, {...someData}, {responseType: 'blob'})
    .then((res) => {
        //...
    }).catch((err) => {
        //
    })
服务端如何返回数据
// 这里以express举例
const fs = require('fs')
const express = require('express')
const bodyParser = require('body-parser')

const app = express()
app.use(bodyParser.urlencoded({extended: false}))
app.use(bodyParser.json())

// 以post提交举例
app.post('/info/download', function(req, res) {
    const filePath = './file/测试.jpeg'
    const fileName = '测试.jpeg'
    
    res.set({
        'content-type': 'application/octet-stream',
        'content-disposition': 'attachment;filename=' + encodeURI(fileName)
    })

    fs.createReadStream(filePath).pipe(res)
})

app.listen(3000)

结论:使用 express 模拟请求下载接口,返回流文件的时候只需要统一设置 content-type: 'application/octet-stream' 即可,不需要因为不同的文件类型设置不同的 content-type。
另外对于 content-disposition 在设置 filename 的时候,需要对 filename 进行转码,防止下载的文件名中有中文时出现乱码。

前端解析数据流

前端解析数据流,主要使用 Blob 对象来进行接收

// 以之前的post请求举例
axios.post(url, {...someData}, {responseType: 'blob'})
    .then((res) => {
        const { data, headers } = res
        const fileName = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1')
        // 此处当返回json文件时需要先对data进行JSON.stringify处理,其他类型文件不用做处理
        //const blob = new Blob([JSON.stringify(data)], ...)
        const blob = new Blob([data], {type: headers['content-type']})
        let dom = document.createElement('a')
        let url = window.URL.createObjectURL(blob)
        dom.href = url
        dom.download = decodeURI(fileName)
        dom.style.display = 'none'
        document.body.appendChild(dom)
        dom.click()
        dom.parentNode.removeChild(dom)
        window.URL.revokeObjectURL(url)
    }).catch((err) => {})

结论:使用 Blob 对返回的 stream 进行解析,同时使用 URL.createObjectURL 将 blob 转换为一个 url 再进行下载,下载完毕后再对 url 进行释放

剩下的一种常见情况是,后台返回的流文件是一张图片,那么前端如何展示这张图片,代码大概是下面的样子

axios.post(url, {...someData}, {responseType: 'blob'})
    .then((res) => {
        const { data } = res
        const reader = new FileReader()
        reader.readAsDataURL(data)
        reader.onload = (ev) => {
            const img = new Image()
            img.src = ev.target.result
            document.body.appendChild(img)
        }
    }).catch((err) => {})

结论:使用 FileReader 对象来读取 stream 文件,将文件解析成一个 base64 的链接,最后在将 img 指向这个链接即可

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Stella981 Stella981
3年前
AssemblyScript 入门指南[每日前端夜话0xEB]
每日前端夜话0xEB每日前端夜话,陪你聊前端。每天晚上18:00准时推送。正文共:2459 字预计阅读时间:10分钟作者:DannyGuo翻译:疯狂的技术宅来源:logrocket!(https://oscimg.oschina.net/oscnet/b880277c594152a503
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
Node.js 12中的ES模块[每日前端夜话0x9E]
每日前端夜话0x9E每日前端夜话,陪你聊前端。每天晚上18:00准时推送。正文共:2552字预计阅读时间:10 分钟作者:BrianDeSousa翻译:疯狂的技术宅来源:logrocket!(https://oscimg.oschina.net/oscnet/2ccaf94cecd3
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这