解决前端跨域下载文件问题

7,743 阅读1分钟

HTML download 属性

在同源下载是没有问题的
<a href="http://localhost:8000/test.png" download="test.png"> //本地同源


工作中附件可能是多个不同域名下返回的


<a href="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/v3/static/img/logo.a7995ad.svg~tplv-t2oaga2asx-image.image" download="a7995ad.svg">
//本地下载掘金logo
这种下载方法 项目中会用到多次,所以我们来封装一个公共方法。

获取文件名

function getFileName(url) {    
    const name = url.split('/');    
    return name.pop();
}

获取到文件内容  创建 blob对象,并转换为 blob string

fetch(url).then(response=>{    
    return response.blob();
})
.then(blob=>{   

   //转换为 blob string 
    const blobUrl = window.URL.createObjectURL(blob); //blob:http://localhost:8000/59f8792c-c863-4bcc-b891-6fedb25e29bd


    //然后我们创建a标签
   const blobUrl = window.URL.createObjectURL(blob);

   const tempLink = document.createElement('a');

   tempLink.style.display = 'none';

   tempLink.href = blobUrl;
    
   document.body.appendChild(tempLink);

   //触发点击事件
   tempLink.click();

   //触发下载之后清掉
   setTimeout( ()=> {   
    URL.revokeObjectURL(blobUrl);     
    document.body.removeChild(tempLink);
   });
}


我们在整合一下通用的方法


/**
* 
* @param url  附件地址
* @param download  附件可以预览或者下载
*/

function fileDownload(url, download = true) {

    function getFileName(url) {    
         const name = url.split('/');            return name.pop();    }

    const filename = getFileName(url);

    fetch(url)

    .then(response=>{    

        return response.blob();

    })
    .then(blob=>{     

       const blobUrl = window.URL.createObjectURL(blob);
   
       const tempLink = document.createElement('a');

       tempLink.style.display = 'none';

       tempLink.href = blobUrl;  
   
   if (download) {    
     //下载       
     tempLink.setAttribute('download', filename);
   }else{
      //预览       
      tempLink.setAttribute('target', '_blank');
   }  
   document.body.appendChild(tempLink);

   tempLink.click();

   setTimeout( ()=> {  
 
     URL.revokeObjectURL(blobUrl);
     document.body.removeChild(tempLink);

   })}
}

我们的通用下载就写好了。 

方法的核心就在获取附件之后用 createObjectURL 把附件转为blob.强制附件url使其同源。

总结

  • fetch 可使用 axios 或者 原生XMLHttpRequest 等其他方式代替,只要能正确获取到blob对象。
import axios from 'axios';
axios.get(url, {    responseType: 'blob'})
.then(response=>{
    // ...code
})


const xMLHttpRequest = new XMLHttpRequest();
xMLHttpRequest.open("GET", url, true);
xMLHttpRequest.responseType = 'blob';
xMLHttpRequest.onload = function () {

    // ...code

}

xMLHttpRequest.send();
  • 获取文件名也可以自己喜欢的方式和库去实现
import {split, last, nth, partialRight, flow} from 'lodash';

const getFileName = flow([partialRight(split, '/'), last]);

const filename = getFileName(url);