前端各种下载方式的对比

222 阅读4分钟

在前端常见的业务场景中, 下载场景是很普遍的一种业务需求,前端下载方式非常多,每种方式各有适用它的场景。 下面针对最常见的前端下载场景, 做了使用比较, 方便大家在合适的场景中, 选择最适合的下载方式。

window.open/location.href

非常常见的前端下载方式, 这种方式为后端生成文件,将文件直接传递到前端来,通常称之为后端下载. 适用于文件由后端生成的情况

window.open('http://localhost:3000/file') // 后端需要设置文件下载头部, 才能正常下载 Content-Disposition: attachment;

// 参数携带
window.open('http://localhost:3000/file?name=lisi')

location.href = 'http://localhost:3000/file'
location.href = 'http://localhost:3000/file?name=lisi'

劣势:

  • 请求方式只能是 get , 因此数据的携带量较小, 且安全性较低, 对于需要携带数据量稍微大一点的请求不适用

  • 在异步情况下使用时, 会受到浏览器安全策略的拦截, 因为是通过脚本进行的窗口打开,可能会被认为是广告或者有安全问题,拦截弹窗或者询问是否允许下载。

    const Title = () => {
      const fun = () => {
        setTimeout(() => {
          window.open('http://localhost:3000/file?name=123')
        }, 1000)
      }
      return <h1 onClick={fun}>下载文件</h1>
    }
    
    <Title/>
    

优势:

  • 使用方便, 可定义打开在窗口或者当前窗口
  • 没有跨域问题, 不同域下依然能够使用

iframe

创建 iframe 这种文件下载的方式在实际生产过程中其实不太常用, 原理类似 window.open/location.href, 只是使用方式上略有不同, 大多数的情况下, 大家会选择使用 a 链接去下载文件

// 使用 iframe 
let iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'http://localhost:3000/file?name=lisi'
document.body.appendChild(iframe);

劣势:

  • get 请求方式, 携带数据量有限

优势:

  • 没有跨域问题, 不同域下依然能够使用

a链接

创建 a 链接,或者使用 a 标签直接设置 download 属性进行下载,这种方式是前端最普遍使用的方式。

// 创建 a 链接
let dom = document.createElement('a');
dom.style.display = 'none';
dom.download = '';
dom.href = 'http://localhost:3001/file?name=123'
document.body.appendChild(dom);
dom.click();
document.body.removeChild(dom);

// 或者直接在 a 链接上使用 download 属性
<a href='http://localhost:3001/file?name=123' download >

劣势:

  • get 请求方式, 携带数据量有限

优势:

  • 没有跨域问题, 不同域下依然能够使用

URL.createObjectURL

这种前端的下载处理方式为现代浏览器新增的使用方式, 但是好消息是几乎大部分的浏览器现在都支持, 这种方式为纯前端的下载方式, 文件在前端生成, 然后浏览器下载前端生成的文件。

当调用 createObjectURL() 时, 会生成一个指向指定 file 对象或者 Blob 的 URL, 得到 URL 后, 使用 下载方式 3 进行文件下载

const type = "application/json", name = "file.json";
const data = 'download file';
const blob = new Blob([data], {type});
const url = window.URL.createObjectURL(blob);

// 使用完毕后,安全的时间点清除 URL
window.URL.revokeObjectURL(url);

或者我们在通常的使用中,文件非前端产生, 而是通过后端传递文件流

fetch('http://localhost/file')
  .then(resp => resp.blob())
  .then(blob => {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    a.download = 'example.json';
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);
  })

劣势:

  • 文件生成方式为前端, 仅适用文件大小比较小的文件, 大文件会造成前端性能问题
  • 在 IE 浏览器环境下 URL.createObjectURL 有兼容性问题
  • 和服务器交互时, 有跨域问题

优势:

  • 纯前端下载时, 不用和后端交互, 使用起来方便
  • 和服务器有交互时, 传递的数据方式可以为 get/post 等, 携带的数据量比较比较大

Form Post

这种下载方式使用原生的 form ,通过表单 submit, 传递数据到服务器端

const downloadForm = document.createElement('form');
downloadForm.method = 'post';
downloadForm.target = '_blank';
downloadForm.hidden = true;
downloadForm.action = url;
// 这里可以携带数据, 但是携带的数据,必须得通过 表单元素的方式附加到 form 里面

document.body.appendChild(downloadForm);
downloadForm.submit();
document.body.removeChild(downloadForm);

劣势:

  • 使用时稍微有点麻烦, 数据需要添加到 form 里面

  • 异步场景里面使用时会被安全策略阻止下载

    setTimeout(() => {
        // 在这里使用 form post 下载
    }, 1000)
    

优势:

  • 没有跨域问题
  • 可以通过 post 传递更多数据