30行JS代码实现浏览器文件下载

754 阅读2分钟

“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第3篇文章,点击查看活动详情

背景

前端日常开发过程中,经常会遇到 下载文件 的需求。

之前常使用的方法

  1. 收集所需参数传递给前置请求,请求返回文件下载地址或者文件id。
  2. 利用 a 链接 download 属性 实现文件下载。

那么有没有方法直接实现文件下载那? 可以有。
就是让后端直接返回 blob文件流,前端发起一个请求实现对应下载。 本文主要介绍对应功能的实现。

实现思路

  1. 前端发起网络请求,指定响应类型,拿到后端返回的文件流,文件名。
  2. 利用 new Blob() 创造一个 Blob 对象
  3. 利用 URL.createObjectURL(blob) 创造url地址。
  4. 创建 a 元素,指定 href,download 等属性, 模拟点击实现下载。
  5. 利用 URL.revokeObjectURL() 释放url地址。
  6. 利用 removeChild 移除 a 元素。

函数封装

import axios from 'axios';

/**
 * 发送 post 请求下载文件,文件返回的为blob流
 * @param {*} url    接口地址
 * @param {*} data   请求参数
 * @param {*} fileName 文件名 
 */

export default async function fileDownload(url, data, fileName) {
  try {
    await axios({
      url: url,
      method: 'post',
      data: JSON.stringify(data),
      responseType: 'blob', //指定blob
      headers: {
        'Content-Type''application/json;',
        // token 等其它所需请求头
      }
    }).then((res)=> {
      const content = res.data;
      const contentType = res.headers['content-type'] || 'application/octet-stream';//解决兼容性
      const blob = new Blob([content], {type:contentType});
      // 文件名一般后端会返回,调用方法时不需要传入。如果不返回,需要前端传入
      // 具体在header里面那个字段返回需要协商, 此处只是一种方式,可自行修改。
      if (!fileName) { //未传入fileName, 从header里面取
        if (res.headers['fileName']) {
          fileName = decodeURI(res.headers['fileName']); // 注意解码
        }
      }
      if ('download' in document.createElement("a")) { //非ie
        const fileLink = document.createElement("a");
        fileLink.download = fileName;
        fileLink.style.display = 'none';
        fileLink.href = URL.createObjectURL(blob);
        document.body.appendChild(fileLink);
        fileLink.click();
        URL.revokeObjectURL(fileLink.href); //释放url对象
        document.body.removeChild(fileLink);
      } else {
        navigator.msSaveBlob(blob, fileName);
      }
    });
  } catch(e) {
  }
}

注意:application/octet-stream 解决火狐等兼容性。

函数调用

调用对应函数,传入对应参数即可。

let isDownloading = true;
fileDownload(url, data).then(()=>{
  isDownloading = false;
})

总结

按照对应思路实现即可,每天进步一点点。

点赞评论是更新的动力,jym 动动小手支持下吧!