前端下载文件的几种方式

6,996 阅读2分钟

盘点几种前端下载文件的方式,location.href,window.open,iframe,form表单,二进制流,a标签超链接。分别总结下不同的特点及优势。

location.href

location.href直接指向一个文件的话,浏览器会下载该文件,对于单文件下载没有什么问题,但是如果下载多文件,点击过快就会重置掉前面的请求

const download = (url) => {
   window.location.href = url;
}

window.open

window.open可以打开一个新窗口,虽然能通过这种方式下载文件,但是新的窗口不会关闭,明显体验上不好

const download = (url) => {
   window.open(url);
}

iframe

iframe也可以向服务器发请求的,并且不会因为多次点击而重置之前的链接,可用于多文件下载

const download = (url) => {
  const iframe = document.createElement("iframe");
  iframe.style.display = "none"; // 防止影响页面
  iframe.style.height = 0; // 防止影响页面
  iframe.src = url; 
  document.body.appendChild(iframe); // 这一行必须,iframe挂在到dom树上才会发请求
  // 5分钟之后删除(onload方法对于下载链接不起作用,就先抠脚一下吧)
  setTimeout(()=>{
    iframe.remove();
  }, 5 * 60 * 1000);
}

form表单

利用form表单提交能发起浏览器请求,并且也可以作为多文件下载来使用

const download = (url) => {
	var params = {// 参数
        id:xx,
        name:xx
    };
 
    var form = document.createElement('form')
    form.id = 'form'
    form.name = 'form'
    document.body.appendChild (form)
    for (var obj in params) {
        if (params.hasOwnProperty(obj)) {
            var input = document.createElement('input')
            input.tpye='hidden'
            input.name = obj;
            input.value = params[obj]
            form.appendChild(input)
        }
    }
    form.method = "GET" //请求方式
    form.action = url
    form.submit()
    document.body.removeChild(form)
}

二进制流

有时候后端返回是一个Blob的文件流形式,那么前端需要自己将文件流转成链接,然后下载

const download = (content, fileName) => {
  const blob = new Blob([content]);
  const a = document.createElement("a");
  const url = window.URL.createObjectURL(blob);
  const filename = fileName;
  a.href = url;
  a.download = filename;
  a.click();
  window.URL.revokeObjectURL(url);
}

a标签

a标签及href指向的如果是一个下载链接,那么相当于下载文件,对于单文件下载还是ok的,不过快速点击几个下载按钮,有的下载会被Cancelled,这可不行

const download = (url, fileName) => {
	const a = document.createElement("a");
    a.href = url;
  	a.download = fileName;
    a.click();
}

form表单校验

function checkFormRules(rules, d) {
  let result = false;
  const ruleObj = {
    required: (_, val) => !val,
    len: (base, val) => base != val.length,
    min: (base, val) => base > val.length,
    max: (base, val) => base < val.length,
    pattern: (base, val) => !base.test(val),
  }
  outer:
  for (let i in rules) {
    inter:
    for (let j in rules[i]) {
      const item = rules[i][j]
      const itemKeys = Object.keys(item).filter(key => Object.keys(ruleObj).includes(key))
      if (itemKeys.filter(key => ruleObj[key](item[key], d[i])).length>0) {
        result = item['message']
        break outer;
      }
    }
  }
  return result
}

lodash封装

const lodash = {}

const arrayExtend = {}
const stringExtend = {}
const objectExtend = {
  get(obj, key, defaultValue) {
    let result = obj;
    for (let k of key.split('.')) {
      if (result[k]) {
        result = result[k]
      } else {
        result = false
        break;
      }
    }
    return result || defaultValue
  }
}
const functionExtend = {}
const mathExtend = {}
const numberExtend = {}

const extendType = [
  {
    type: Array,
    children: [
      'join', 
      'concat'
    ],
    extendFuns: arrayExtend
  },
  {
    type: String,
    children: [
      'split',
      'trim'
    ],
    extendFuns: stringExtend
  },
  {
    type: Object,
    children: [
      'assign'
    ],
    extendFuns: objectExtend
  },
  {
    type: Function,
    children: [
      'bind'
    ],
    extendFuns: functionExtend
  },
  {
    type: Math,
    children: [
      'min'
    ],
    extendFuns: mathExtend
  },
  {
    type: Number,
    children: [
      'toFixed'
    ],
    extendFuns: numberExtend
  },
]

extendType.forEach(item => {
  const { children = [], extendFuns = {} } = item
  for (let key of children) {
    lodash[key] = (obj, ...args) => item.type.prototype[key].call(obj, ...args)
  }
  for (let key in extendFuns) {
    lodash[key] = extendFuns[key]
  }
})

console.log(lodash.get({user:{name: '55'}}, 'user.name'))
console.log(lodash.join([1, 2, 3], '-'))