纯前端跨域下载pdf链接⽂件解决⽅案
⼀,介绍与需求
1.1,介绍
XMLHttpRequest ⽤于在后台与服务器交换数据。这意味着可以在不重新加载整个⽹页的情况下,对⽹页的某部分进⾏更新。
问题:Chrome 会⾃动调⽤内置的 pdf 阅读器打开
1.2,需求
在⾕歌(Chrome)浏览器中,使⽤a标签属性download下载pdf链接⽂件,如果是相同域时,可以直接下载;但是如果域不同,则不是下载,⽽是直接打开页⾯预览⽂件。但是需求是直接点击下载⽂件,⽽不是打开预览;以及下载后台返回的⽂件流。
⼆,下载⽂件
已发布npm包:,运⾏如下命令即可安装使⽤
cnpm install web-downloadfile --save
⽬前只提供三个Api,分别如下:
import { base64ToFileOrBlob, saveFileToBlob, saveFileToLink } from 'web-downloadfile';
详细的使⽤⽅式可查看
2.1,思路
通过a标签的download属性,我们可以直接下载后台接⼝返回的数据流⽂件;故此,我们是否可以模拟发送http请求,将⽂件链接转换成⽂件流来使⽤a标签download下载。以下主要介绍链接⽂件转⽂件流下载的思路与⽅法
2.2,⽂件路径转⽂件流
1,先校验是否是路径链接
使⽤正则表达式校验url是否合法
1 let reg = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+).)+([A-Za-z0-9-~\/])+$/;
2  if (!st(url)) {
3    throw new Error("传⼊参数不合法,不是标准的链接");
4  }
2,创建XMLHttpRequest对象
模拟发送http请求,获取⽂件流
1  let xhr = new XMLHttpRequest();//创建 XMLHttpRequest 对象
2  xhr.open('get', 'url', true);//规定请求的类型、URL 以及是否异步处理请求。三个参数分别是 method:请求的类型;GET 或 POST url:⽂件在服务器上的位置 async:true(异步)或 false(同步)
3  xhr.setRequestHeader('Content-Type', `application/pdf`);//设置请求头
4  sponseType = "blob";//返回的数据类型这⼉需要blob对象
5  load = function () {//请求成功回调函数
6      if (this.status == 200) {
7        //接受⼆进制⽂件流
8        var blob = sponse;
9      }
10    }
11  xhr.send();//将请求发送到服务器
3,完整⽅法
按 Ctrl+C 复制代码
2.3,下载⽂件
1,创建下载链接
1 let downloadElement = ateElement('a');
2 let href = blob;
3  if (typeof blob == 'string') {
4    downloadElement.target = '_blank';//如果是链接,打开新标签页下载
5  } else {
6    href = ateObjectURL(blob); //创建下载的链接
7  }
8  downloadElement.href = href;//下载链接
2,模拟点击下载链接
1 downloadElement.download = tagFileName + moment(new Date().getTime()).format('YYYYMMDDhhmmss') + '.' + fileType; //下载后⽂件名
2 document.body.appendChild(downloadElement);
3 downloadElement.click(); //点击下载
3,下载完成后释放资源
chrome直接下载
1  veChild(downloadElement); //下载完成移除元素
2  if (typeof blob != 'string') {
3    vokeObjectURL(href); //释放掉blob对象
4  }
4,完成⽅法
按 Ctrl+C 复制代码
2.4,base64对象转⽂件对象
主要针对图⽚,不过其他⽂件也可
1 /**
2  * base64对象转⽂件对象
3  * @param urlData  :数据的base64对象
4  * @param type  :类型 image/png;
5  * @returns {Blob}:Blob⽂件对象
6  */
7 function base64ToBlob(urlData, type) {
8  let arr = urlData.split(',');
9  let array = arr[0].match(/:(.*?);/)
10  let mime = (array && array.length > 1 ? array[1] : type) || type;
11  // 去掉url的头,并转化为byte
12  let bytes = window.atob(arr[1]);
13  // 处理异常,将ascii码⼩于0的转换为⼤于0
14  let ab = new ArrayBuffer(bytes.length);
15  // ⽣成视图(直接针对内存):8位⽆符号整数,长度1个字节
16  let ia = new Uint8Array(ab);
17  for (let i = 0; i < bytes.length; i++) {
18    ia[i] = bytes.charCodeAt(i);
19  }
20  return new Blob([ab], {
21    type: mime
22  });
23 }
2.5,使⽤实例
1,⽂件链接转⽂件流下载
1 fileLinkToStreamDownload('127.0.0.1/download.pdf', '下载⽂件实例', 'pdf')
2,base64对象转⽂件对象下载
1 let blob = base64ToBlob('data:image/png;base64,iVBORw0KGgo=...','image/png')//获取图⽚的⽂件流
2 downloadExportFile(blob, 'download', 'png')
问题记录:浏览器缓存问题
由于浏览器的缓存机制,当我们使⽤XMLHttpRequest发出请求的时候,浏览器会将请求的地址与缓存中的地址进⾏⽐较,如果存在相同记录则根据不向服务器发出请求⽽直接返回与上⼀次请求相同内容。
解决这类缓存问题的办法:
1,时间戳⽅法 —即在每次请求的url后⾯加上当前时间的字符串或其他类似的不会重复的随机字符串,这样浏览器每次发出的是不同的url,即会当做不同的请求来处理,⽽不会从缓存中读取。
1  if(url.indexOf("?")>=0){//判断url中是否已经带有参数
2        url = url + "&t=" + (new Date()).valueOf();
3  }else{
4        url = url + "?t=" + (new Date()).valueOf();
5      }
2,在XMLHttpRequest发送请求之前加上:
加If-Modified-Since头
1 xhr.setRequestHeader("If-Modified-Since","0"); 
2 xhr.send(null);

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。