你真的会用 filesave 这个插件吗?

前言

关于 FileSaver 这个插件不少人都知道是用来保存文件的, 但是你可能不知它会在通过url保存文件的时候通过 HEAD 的请求方法来确认资源是否真正可以访问。当HEAD方法成功之后,接下来才会真的去执行下载逻辑; 下面是插件的源码, 在 corsEnabled 函数里面会发送一个 HEAD 方法


function corsEnabled (url) {
  var xhr = new XMLHttpRequest()
  // use sync to avoid popup blocker
  xhr.open('HEAD', url, false)
  try {
    xhr.send()
  } catch (e) {}
  return xhr.status >= 200 && xhr.status <= 299
}


function saveAs (blob, name, opts) {
    var URL = _global.URL || _global.webkitURL
    var a = document.createElement('a')
    name = name || blob.name || 'download'

    a.download = name
    a.rel = 'noopener' // tabnabbing

    // TODO: detect chrome extensions & packaged apps
    // a.target = '_blank'

    if (typeof blob === 'string') {
      // Support regular links
      a.href = blob
      if (a.origin !== location.origin) {
        corsEnabled(a.href)
          ? download(blob, name, opts)
          : click(a, a.target = '_blank')
      } else {
        click(a)
      }
    } else {
      // Support blobs
      a.href = URL.createObjectURL(blob)
      setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4) // 40s
      setTimeout(function () { click(a) }, 0)
    }
  }

  // Use msSaveOrOpenBlob as a second approach
  : 'msSaveOrOpenBlob' in navigator
  ? function saveAs (blob, name, opts) {
    name = name || blob.name || 'download'

    if (typeof blob === 'string') {
      if (corsEnabled(blob)) {
        download(blob, name, opts)
      } else {
        var a = document.createElement('a')
        a.href = blob
        a.target = '_blank'
        setTimeout(function () { click(a) })
      }
    } else {
      navigator.msSaveOrOpenBlob(bom(blob, opts), name)
    }
  }

错误使用

想象一下你这个时候通过一个 url 来下载一个表格, url 上面有一些查询参数, 当这个后台接收到请求之后,会查询数据库, 经过一系列的逻辑处理终于打成目标, 可以导出一个表格了!!! 结果呢, 卧槽竟然只是一次HEAD 方法试探, 没有办法, 服务端还是要再来一遍之前的逻辑, 把结构返回给后续的GET 方法, 所以如果你需要下载的资源是实时生成的可能不太适合使用 filesave 直接下载, 可以通过 ajax 发送请求, 然后将返回的数据导出。

正确使用

像图片这种资源, 还有前端导出表格就比较适合 FileSaver 这个插件

适合我使用场景的demo

这里我需要一个下载状态, 所以通过发送一个get请求, 然后将返回的数据稍作处理进行导出表格, 并不是直接通过a标签导出的

export default (opt = { url: '', method: 'get' }, fileName) => {
  return request({
    ...opt,
    responseType: 'blob'
  }).then(res => {
    const link = document.createElement('a')
    link.href = URL.createObjectURL(res.data)
    link.download = fileName
    link.click()
    return true
  })
}

解释

HEAD 方法: HEAD方法和GET方法一样,只是不返回报文主体部分。用于确认URI的有效性及资源更新的日期时间等; 在<<图解http>> 这本书里面有解释

posted @ 2022-05-31 14:13  阿臻  阅读(236)  评论(0编辑  收藏  举报