• Vue中如何实现文件下载功能
  • 发布于 2个月前
  • 226 热度
    0 评论
简介
巩固基础,非止于CV实现文件下载。这句话我想表达的意思是,我们不仅仅只会从堆代码或者其他网站上复制别人封装或者写好的插件方法来用,却不知道其具体的实现原理,以及所用到的属性方法的含义,这就是止步于copy and paste。 (本文主要总结文件下载思路和文件不同格式的预览方式)

当同一种实现原理可以完成不同需求任务的时候,我们却只知道通过需求的关键词去网上查找解决方案,却不知我们之前的实现另一种需求的方式方法也能同时应用到当前需求上来,这很大程度上跟自个的基础能力相关;基础不牢固,有病乱投医,是不是这个理?


我们在上初高中的时候,听数学老师说得最多的一句话,什么万变不离其宗,举一反三,那也是建立在你完全把原理理解透彻的基础上的。
所以接下来:
1.先解读下文件(包含图片)所涉及到的属性方法的含义和用途,以及相关概念等知识,
2.然后比对实际需求
3.最后代码实现及总结

文件下载思路
一、使用axios请求将文件转化为blob对象的二进制数据流下载
axios({
    method: 'get',  
    params: {
      file_url: re.data_url,
      file_name: re.data_name
    },
    url: `https:/xxx.com${url}`, 
    // for example: http://dev-head-api.xht-kyy.com/downSkuFile?id=2196
    // 堆代码 duidaima.com
    // 必须显式指明响应类型是一个Blob对象,这样生成二进制的数据,
    //才能通过window.URL.createObjectURL进行创建成功
    responseType: 'blob',
}).then((res) => {
    if (!res) {
        return
    }
    // 将blob对象转换为域名结合式的url
    let blobUrl = window.URL.createObjectURL(res.data)
    let link = document.createElement('a')
    document.body.appendChild(link)
    link.style.display = 'none'
    link.href = blobUrl
    // 设置a标签的下载属性,设置文件名及格式,后缀名最好让后端在数据格式中返回
    link.download = 'xx.csv'
    // 自触发click事件
    link.click()
    document.body.removeChild(link)
    window.URL.revokeObjectURL(blobUrl);
})
通过在body中增加个a标签,并立即执行点击事件,进行跳转访问url,get到的内容为blob文件下载。代码中提到设置a标签的下载属性,设置文件名及格式,后缀名最好让后端在数据格式中返回,那么后端返回的后缀和文件名该如何提取?
可以在请求返回的.then(res)中提取到,其中res.data是blob对象的二进制数据流,res.header包含了文件信息,如下图:

可以通过 res.headers['content-disposition'] 获取到filename中的文件名
改进后的blob下载方法:
outImport (re) {
  this.$axios({
    method: 'get',  
    params: {
      file_url: re.data_url,
      file_name: re.data_name
    },
    url: `https:/xxx.com${url}`, 
    responseType: 'blob'
  }).then((res) => {
    console.log(res)
    let str = res.headers['content-disposition']
    str = str.substr(str.indexOf('=') + 1, str.length)
    this.downloadFile(res.data, decodeURI(str))
  })
},
downloadFile (blob, fileName) {
  if (window.navigator.msSaveOrOpenBlob) {
    navigator.msSaveBlob(blob, fileName)
  } else {
    const link = document.createElement('a')
    const evt = document.createEvent('HTMLEvents')
    evt.initEvent('click', false, false)
    
    document.body.appendChild(link)
    link.href = URL.createObjectURL(blob)
    link.download = fileName
    link.style.display = 'none'
 
    link.click()
    document.body.removeChild(link)
    window.URL.revokeObjectURL(link.href)
  }
},
这里涉及到几个属性:
msSaveBlob()
Internet Explorer 10 的 msSaveBlob 和 msSaveOrOpenBlob 方法允许用户在客户端上保存文件,方法如同从 Internet 下载文件,这是此类文件保存到“下载”文件夹的原因。
URL.createObjectURL()和URL.revokeObjectURL()
URL.createObjectURL可以得到当前文件的一个内存URL, 即返回一段带hash的url,按我的理解就是该链接直接指向存储内存中的数据,并且一直存储在内存中,所以当不使用后,需要清除下,防止内存泄露,那么就有了下面这些清除方法

URL.createObjectURL(file)存在于当前doucment内,清除方式只有unload()事件或revokeObjectURL()手动清除 。


二、使用fetch请求进行下载
使用fetch替代a标签,进行下载MP3文件,在浏览器中说使用a标签会直接跳转页面进行播放,达不到下载的目地
下载服务器的MP3文件
export const downloadMp3 = (filePath) => {
  fetch(filePath).then(res => res.blob()).then(blob => {
    const a = document.createElement('a');
    document.body.appendChild(a)
    a.style.display = 'none'
    // 使用获取到的blob对象创建的url
    const url = window.URL.createObjectURL(blob);
    a.href = url;
    // 指定下载的文件名
    a.download = '语音音频.mp3';
    a.click();
    document.body.removeChild(a)
    // 移除blob对象的url
    window.URL.revokeObjectURL(url);
  });
}
这里涉及到fetch, 简要总结下fetch的使用(结合上述代码所使用的情况),也和方法一种axios的方法做一个简要的比较:
fetch 是浏览器内置的 API,用于发送网络请求。它提供了一种现代化、基于 Promise 的方式来进行网络通信。与 axios 类似,fetch 也提供了一种较低级别的封装,但相比于 axios,它的功能和语法更为简单。fetch 通过链式调用的方式设置请求参数从上述代码中就可以看出来,返回的是一个 Promise 对象
另外:fetch也提供了第二个参数,用于设置相关的请求头、方式、类型等相关配置入参
如下参考:
{
  method: "GET",//请求方式
  headers: {//定制http请求的标头
    "Content-Type": "text/plain;charset=UTF-8"
  },
  body: undefined,//post请求的数据体,因为此时为get请求,故为undefined
  referrer: "about:client",
  referrerPolicy: "no-referrer-when-downgrade",//用于设定fetch请求的referer标头
  mode: "cors", //指定请求模式,此时为cors表示支持跨域请求
  credentials: "same-origin",//发送cookie
  cache: "default",//指定如何处理缓存
  redirect: "follow",
  integrity: "",
  keepalive: false,
  signal: undefined 
}
而其链式调用方式:
fetch('网址')
// 堆代码 duidaima.com
// fetch()接收到的response是一个 Stream 对象
// response.json()是一个异步操作,取出所有内容,并将其转为 JSON 对象
  .then(response => response.json()) 
  .then(json => console.log(json))//获取到的json数据
  .catch(err => console.log('Request Failed', err)); 

后续写一篇针对该方法的总结。


文件不同格式的预览方式
封装一个预览与下载相结合的方法,实现不同文件格式的预览方式
思路:将文件名解剖,取文件类型后缀,通过枚举方式调用不同类型的预览方法
    thumbnailView (item) {
      item.name = item.original_name
      const suffix = item.name.substring(item.name.lastIndexOf('.') + 1)
      if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'tiff', 'webp'].includes(suffix)) {
        this.preView(item)
      } else if (['xlsx', 'xls', 'doc', 'docx', 'ppt', 'pptx'].includes(suffix)) {
        this.viewNew(item, suffix)
      } else if (['pdf', 'PDF'].includes(suffix)) {
        this.viewNewPDF(item)
      } else {
        this.showConfirm(item)
      }
    },
    preView (val) {
      this.$refs.imageView.open(val.file_name)
    },
    preView2 (val) {
      this.$refs.imageView.open(val)
    },
    viewNewPDF (item) {
      window.open(item.file_name, '_blank')
    },
    showConfirm (item) {
      this.$confirm({
        title: '提示',
        content: h => <div>该文件不支持预览、请下载后查阅</div>,
        okText: '下載文件',
        onOk: () => {
          saveAs(item.file_name, item.name)
        }
      })
    },
    viewNew (val, suffix) {
      window.open(`https://view.officeapps.live.com/op/view.aspx?src=${val.file_name}`, '_blank')
    },

saveAs
上述代码中涉及到一个插件:saveAs,这里简要介绍下:
import { saveAs } from 'file-saver';
saveAs(Blob/File/Url, optional DOMString filename, optional Object { autoBom }
第一个参数支持三种类型,这也时下载文件的一个插件
总结
掌握:axios和fetch的用法
学习 msSaveBlob和URL.createObjectURL() 和 revokeObjectURL()
响应blob数据类型转换

用户评论