【前端】开发日常——下载弹窗闪退

496 阅读1分钟

开发日常——下载弹窗闪退

记录一下前几天遇到的一个问题,用户在点击下载时,前端会发起一个请求,后端返回下载链接。

响应格式可能是这样的,前端要做的处理也很简单,只需要window.open(res.data)即可触发下载。

{  
"status": 0,  
"success": true,  
"message": "SUCCESS",  
"data": "http://xxx.xxx.com/xxx.exe"  
}

问题出现在前不久,线上的dev环境在window.open的时候突然打开一个窗口没有触发下载就闪退了,验证返回的下载链接是有效的,诡异的是在本地开发环境下是可以直接下载的。

控制台并没有任何报错,怀疑可能是window.open被浏览器阻止了,于是使用了a标签下载,未果。又尝试使用了一个download的工具函数如下:

/**
 * 获取 blob
 * @param  {String} url 目标文件地址
 * @return {Promise}
 */
function getBlob(url: string) {
  return new Promise<Blob>((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    xhr.open('GET', url, true);
    xhr.responseType = 'blob';
    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.response);
      }
    };
    xhr.onerror = () => {
      reject('getBlob error');
    };

    xhr.send();
  });
}

/**
 * 保存
 * @param  {Blob} blob
 * @param  {String} filename 想要保存的文件名称
 */
export function saveAs(blob: Blob, filename: string) {
  // @ts-ignore msSaveOrOpenBlob
  if (window.navigator.msSaveOrOpenBlob) {
    // @ts-ignore msSaveBlob
    navigator.msSaveBlob(blob, filename);
  } else {
    const link = document.createElement('a');
    const body = document.querySelector('body');

    link.href = window.URL.createObjectURL(blob);
    link.download = filename;

    // fix Firefox
    link.style.display = 'none';
    // @ts-ignore
    body.appendChild(link);

    link.click();
    // @ts-ignore
    body.removeChild(link);

    window.URL.revokeObjectURL(link.href);
  }
}

/**
 * 下载
 * @param  {String} url 目标文件地址
 * @param  {String} filename 想要保存的文件名称
 * @param {Function} fallback 下载失败回调
 */
export default function download(url: string, filename: string, fallback?: () => void) {
  getBlob(url)
    .then((blob) => {
      saveAs(blob, filename);
    })
    .catch(() => {
      if (fallback && typeof fallback === 'function') {
        fallback();
      }
    });
}

终于!在控制台看到了报错。(之后尝试了一下location.href也是可以看到报错的)

image.png

原因是在线上域名是https开头,直接去打开一个http的下载链接会触发浏览器的拦截,而本地开发是http开头的可以直接下载,让后端返回一个https开头的链接即可。