薛定谔的Baboon

薛定谔的Baboon 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

薛定谔的Baboon 赞了文章 · 1月12日

使用axios如何下载文件

先上结论,再谈细节:

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

前端发起请求

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

  • 如何传递 responseType?

1587294479(1).jpg

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

  • 为什么要传递 responseType?

xhr.jpg

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

  • responseType 为什么是 'blob'?

responseType.jpg

来源: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 指向这个链接即可

查看原文

赞 5 收藏 2 评论 0

薛定谔的Baboon 回答了问题 · 1月11日

解决html-webpack-plugin 中使用 title选项设置模版中的 值 无效 帮忙看看撒

我也遇到这个问题,是因为我使用了 html-loader ,我对 html-loader 配置了 exclude 属性,排除了,我的 html-webpack-plugin 使用的 模板文件

我的构建结果可以正常输出变量的值了

解决方法

  • html-webpack-plugin 配置
plugins: [
  new CleanWebpackPlugin(),
  new HtmlWebpackPlugin({
    title: "hello HtmlWebpackPlugin!",
    template: "./index.html", // 这是我的模板文件
    inject: true,
  }),
];
  • html-loader 配置
module: {
  rules: [
    {
      test: /.html$/,
      use: {
        loader: "html-loader",
      },
      exclude: /index.html/, //排除 html-webpack-plugin 使用的模板文件,解决变量不输出问题
    },
  ];
}

关注 11 回答 8

薛定谔的Baboon 关注了用户 · 1月8日

夜尽天明 @biaochenxuying

公众号:【全栈修炼】,大前端开发相关的技术文章,热点资源,全栈程序员的成长之路。

公众号:【前端GitHub】,专注于挖掘优秀的前端开源项目,抹平你的前端信息不对称。

转载文章,请联系笔者。

关注 789

认证与成就

  • 获得 0 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 1月8日
个人主页被 81 人浏览