解决因跨域导致使用a标签下载文件download属性失效无法自定义命名的问题

问题背景:

在使用a标签下载文件时,download属性可以更改下载的文件名。

// 下载a.exe,并采用默认命名
点击下载


// 将a.exe改名为b.exe下载
点击下载

但是当a标签的下载链接跨域时,download属性将不会生效,原因是浏览器无法获取到文件,不能对他进行更改。

在这种情况下如果你是下载浏览器无法解析的文件,例如.exe,.xlsx..那么浏览器也会自动下载,但是如果你使用浏览器可以解析的文件,比如.txt,.png....浏览器就会采取预览模式,无法直接下载。

// 无法正常下载a.jpg,会直接预览
点击下载

// 正常下载a.exe文件
点击下载

注意:

html5 新特性a标签download属性只支持谷歌和火狐
在谷歌和火狐浏览器a标签download属性修改文件名失效的原因:不同源,访问的域名和href的域名要一致。

a标签属性文档:锚元素 - HTML(超文本标记语言) | MDN

解决因跨域导致使用a标签下载文件download属性失效无法自定义命名的问题_第1张图片

什么是同源url:

解决因跨域导致使用a标签下载文件download属性失效无法自定义命名的问题_第2张图片

 解决方法:

使用Blob实现文件下载,先把文件以bobl的形式下载到当前页面,再创建a标签。

// 下载url(解决跨域a.download不生效问题)
      downloadFile(url, fileName) {
        const x = new XMLHttpRequest()
        x.open("GET", url, true)
        x.responseType = 'blob'
        x.onload = function(e) {
          const url = window.URL.createObjectURL(x.response)
          const a = document.createElement('a')
          a.href = url
          a.target = '_blank'
          a.download = fileName
          a.click()
          a.remove()
        }
        x.send()
      },

 使用方法:

      
        {{ fileName|| '-' }}
      

这里的validateNull是校验url是否为空:

/**
 * 判断是否为空
 */
export function validatenull(val) {
  if (typeof val === 'boolean') {
    return false;
  }
  if (typeof val === 'number') {
    return false;
  }
  if (val instanceof Array) {
    if (val.length===0) return true;
  } else if (val instanceof Object) {
    if (JSON.stringify(val) === '{}') return true;
  } else {
    if (val==='null' || val===null || val==='undefined' || val===undefined || val==='') return true;
    return false;
  }
  return false;
}

这个下载方法有个需要注意的问题就是,当自定义的fileName内存在小数点时(如 fileName = '附件v1.2.zip'),如果没有在fileName后面加上后缀名(fileName = '附件v1.2'),会导致下载时自动以第一个小数点后面的为文件后缀名(.2),所以这时还要加一个方法判断文件的后缀名,将后缀名加到自定义的文件名后面:

function getFileTypeByPath(pathUrl) {
  let index = pathUrl.lastIndexOf(".") // lastIndexOf(".")  找到最后一个  .  的位置
  let fileType = pathUrl.substr(index + 1) // substr() 截取剩余的字符,即文件后缀名 doc 、jpg 、 xlsx 等
  return fileType
}

此时我们传入的fileName为:

const suffix = this.getFileTypeByPath(fileUrl)
this.suffix = `.${suffix}`
const fileName = '附件v1.2'
this.fileName = `${fileName}${this.suffix}`

你可能感兴趣的:(JavaScript,前端,javascript,vue)