0. 背景
最近遇到一个需求是H5通过微信公众号打开一个pdf链接(url形式,浏览器输入url可直接下载)看上去很简单,不过还是有不少要注意的。
1. 如何打开文件
拿到需求第一想法,怎么打开链接呢?window.open? window.location.href? 方法还是挺多的。MDN回顾.
window.open:指定名字将相关资源加载在浏览器新窗口上。
// pdfUrl:要加载的资源,strWindowName,可选,窗口名称。
let windowObjectReference = window.open(pdfUrl, strWindowName);
我这里只需要填写pdfUrl即可,当然跟打开新网页的效果不同。在pc端实现的效果是直接下载pdf。
但是,但是,第一次做移动端,才涨知识了,苹果端可以直接显示弹窗并下载,安卓端就会出现如下信息:
网上的说法挺多的,也有说在安卓微信公众号可以使用window.open,有说不行的。但我查到的是说以前微信公众号很多不良商家通过window.open打开非法页面,用户体验不好,所以如果通过window.open开启组织弹出窗口的政策(见上图)。然后试了window.location.href
window.location: 只读对象,返回location对象,包含有关文档当前位置的信息
// 两者等价
window.location.href = 'www.baidu.com';
window.location = 'www.baidu.com';
不过这里也有坑(我后来发现可能是pdfUrl的原因还好,还是要注意!),如安卓用window.location.href从微信浏览器跳转原生浏览器无效,查阅网络发现可能是缓存的问题,加上时间戳来刷新缓存。
window.location.href = url + '?time='+((new Date()).getTime());
和领导沟通后,安卓机这块需求变成:微信公众号内,点击“下载”后显示弹窗,引导用户从默认浏览器打开再下载pdf。这里就需要判断用户所处于什么浏览器了(微信内置浏览器/手机原生浏览器)。
2. 适配不同端
判断用户浏览器处于什么环境,JS提供了一个很强大的API: navigator.userAgent
navigator.userAgent: 返回用户现在浏览器的用户代理
以我自己windows端Chrome浏览器为例子
console.log(navigator.userAgent);
// Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36
这样就好说了,通过indexOf,match都可以判断浏览器环境,
// 判断是否移动端
// /i不区分大小写模式
const isMobile = /Mobi|Android|iPhone/i.test(u);
//判断是否是安卓
const isAndroid = navigator.userAgent.indexOf('Android') > -1 || navigator.userAgent.indexOf('Linux') > -1;
//判断是否是ios
const isIOS = !!navigator.userAgent.match(/\(i[^;]+;(U;)? CPU.+Mac OS X/));
// 判断是否是微信
const isWeixin = navigator.userAgent.toLowerCase().indexOf('micromessenger') !== -1;
判断浏览器所处环境可参考此网站:根据UserAgent判断浏览器类型
不过,这个地方有个问题需要注意(MDN上提出:Navigator.userAgent)
这种方式要浏览器提供了尽可能少的信息。不要假设此浏览器这个属性的值在未来版本有相同的值。
如我的安卓手机默认浏览器用这个方法就是ios端(??),不过大部分都可以通过判断进行下载就好,不然测试成本太高了。
所以,领导给我的需求(见标题2)就变成了:
(1)判断是否是移动端,是进(2),不是直接window.location.href下载pdf(后来跟老师聊其实这步可以不要,毕竟是移动端的组,但以防万一还是判断下)
(2)判断是安卓还是苹果,是进(3),不是直接window.location.href下载pdf
(3)判断是在微信内置浏览器还是手机原生浏览器,是微信先显示弹窗,告诉用户点右上角…通过浏览器重新进入该页面下载,如果不是微信浏览器,不显示弹窗直接下载
3. iOS端下载文件名null
在产品测试的时候,发现iOS点击下载后会出现一个情况,window.open(url)有这个问题。
查阅资料看到一个微信公众号官方人员的回答,应该是相关的:
这里弹窗时还没有真正开始下载,显示的文件名依赖请求response header中Content-Disposition的filename,你没有填这个字段,所以拿到的是空的。后面真正开始下载客户端才能拿到文件名
来源: 微信内置浏览器下载pdf标题null
但领导意思是,虽然后端Content-Disposition没有填写,但尽量不让数据提供方改接口内容(毕竟要磕头)。最后他给了一种解决方法。
// 创建XHR对象
const x = new window.XMLHttpRequest();
// 发送XHR请求
x.open('GET', url, true);
// 返回文件类型
x.responseType = 'blob';
x.onload = () => {
const url = window.URL.createObjectURL(x.response);
const a = document.createElement('a');
a.href = url;
a.download = '123345.pdf';
a.click();
};
x.send();
一点点啃:
XMLHttpRequest(XHR):此对象与服务器交互,在XMLHttpRequest可在不刷新页面时请求特定URL,获取数据。AJAX编程中使用
open(): 规定请求方法,请求url,是否异步请求
send(): 请求发到服务器
这样重新发送了请求在ios进行pdf下载进行文件下载时文件名null的问题。
4. 其他
- vconsole查看接口情况,存储信息。当确定要传生产环境了记得注释掉
import VConsole from 'vconsole';
const vConsole = new VConsole();
- 不同页面数据传值的方法:
- vuex(复杂)
- localStorage(不同页面存储内容不相互独立)
- sessionStorage(关页面就没了)
- cookie(不安全,存储数据少)
- 路由传参
当微信内置浏览器打开原生浏览器时是两个浏览器啊,localStorage肯定不同,传参方式用路由传参!
小许继续努力!😃