【Web】在浏览器中进行文件下载的实践记录

在浏览器中进行文件下载的实践记录

方案一(同源)

<a href="//example.com/resource" download="filename.ext"> 下载 a>

attr download 可以指定下载资源(默认)文件名,但仅在同源时有效。

方案二(非同源)

非同源,但允许跨域时,我们同样可以直接使用 a tag:

<a href="//example.com/resource" target="_self"> 下载 a>

要注意的是,点击链接后浏览器进行下载文件的主要行为主要依赖于下面这几个因素:

  • 服务端响应头 Content-Disposition,它可以影响下列行为:

    • 下载:Content-Disposition: attachment
    • 指定下载时的文件名:Content-Disposition: attachment; filename="filename.jpg"
    • 在浏览器内打开(例如 pdf):Content-Disposition: inline
  • 服务端响应头 Content-Type 会影响下载文件的文件名后缀。

  • attr target"_blank" 有时会影响服务端的 Content-Type 头,使用 "_self" 时正常。

    case:前者得到的该响应头 value 为 application/octet-stream,导致文件扩展名推断错误(消失)。

方案三(可跨域)

通过 xhr/fetch 将资源拉到浏览器内存中,然后再触发浏览器的下载流程。

需要在下载过程中给用户以视觉反馈。

const fetchBlob = (url: string): Promise<Blob> => new Promise((resolve, reject) => {
  const xhr = new XMLHttpRequest()
  xhr.open('GET', url, true)
  xhr.responseType = 'blob'
  xhr.onload = () => {
    if (xhr.status === 200) resolve(xhr.response)
    else reject(xhr.statusText)
  }
  xhr.send()
})
export const saveAsAnyFilename = async (originUrl: string, filename: string): Promise<void> => {
  try {
    const blob = await fetchBlob(originUrl)
    if ((window.navigator as any).msSaveOrOpenBlob) { // IE
      (navigator as any).msSaveBlob(blob, filename)
    } else { // modern browsers
      const link = document.createElement('a')
      document.body.appendChild(link)
      link.href = URL.createObjectURL(blob)
      link.download = filename
      link.style.display = 'none'
      link.click()
      window.URL.revokeObjectURL(link.href)
      document.body.removeChild(link)
    }
  } catch {
    try {
      copy(originUrl)
      toast.success('下载失败, 已复制下载链接, 请手动下载')
    } catch {
      Modal.success({
        title: '下载失败, 请手动复制链接下载',
        content: originUrl,
      })
    }
  }
}

方案四(不可跨域)

几乎只能通过服务端代理请求目标资源,可能存在其他比较 hack 的方案,暂时还没找到。

你可能感兴趣的:(Web,前端,javascript,vue.js)