摘要:三浏览器方式相信大家对这个对象也不太陌生,它是标准里的一个二进制数据对象,可以与对象配合,进行文件的下载。其实这样一个简单的,就可以实现浏览器端自己的下载了。
一、背景
最近写了一个react的组件,用来做文件导出。环境是ie10+。
细一点说,就是
1、读取form里的数据
2、向服务端发请求,并下载文件;要求拿到请求状态,如果出错及时反馈给用户。
第一个需求,我们借用了jquery的serializeArray方法,毕竟我们不想再造轮子。那接下来重点说说后面的需求。
二、一般下载文件方式大家在下载的问题的时候,一般来说,会用到
1、window.open(url);
2、window.location.href = url;
3、iframe,其实与window.open类似,但不用开启新的tab
4、a 标签,利用download属性
这些方法,其实极度依赖服务端的正确性。我们可以看看服务端一旦出错的结果。
1、window.open(url)
打开一个带错误信息的页面
2、window.location.href
页面将跳转到一个错误页面
3、iframe
用户感知不到任何变化
4、a标签
直接出现 下载失败
当然,如果response header里有content-disposition字段的话,浏览器都会下载一个带错误信息的文件。这时候,其实我们可以多发一个ajax/fetch请求,先检测下接口状态,然后再取做下载逻辑。但这样就对服务器造成了额外的开销。
这样的体验都不太好,作为一个追求极致体验的程序猿,我们应该重新思考下,如何提升用户体验。
三、浏览器FileAPI方式相信大家对blob这个对象也不太陌生,它是html5标准里的一个二进制数据对象,可以与URL 对象配合,进行文件的下载。
下面是一个最简单的demo(我们暂时不考虑浏览器兼容问题)。
let blob2 = new Blob(["123"]); let url = URL.createObjectURL(blob2); let a = document.createElement("a"); a.download = "test"; a.href = url; a.click();
其实这样一个简单的demo,就可以实现浏览器端自己的下载了。那如何从服务端拿到数据,并下载呢?
这里,我们拿服务端数据,主要是通过fetch,fetch提供了一些api。其中就有一个blob的promise,我们可以把返回的数据转成blob对象,这样就能去下载文件了。
回到最初的需求,我们需要检测接口的状态。其实通过fetch,我们完全可以拿到response的信息,既然都能拿到,那控制权就在我们自手上了。
四、额外收获-进度条按照FileApi的方式,我们是一次性从服务端拿到数据,然后再在浏览器端进行操作。既然是这样,那拿数据的过程是不是就可以显示出进度呢?这个特性,我们用以前传统的下载方式是完全做不到的。
关于进度条,我们可以利用fetch配合reader对象来实现进度条功能,如下:
fetch(url).then(response => { var reader = response.body.getReader(); var headers = response.headers; var totalLength = headers.get("Content-Length"); var bytesReceived = 0; reader.read().then(function processResult(result) { if (result.done) { return; } bytesReceived += result.value.length; console.log(`progress: ${bytesReceived / totalLength * 100}%`); return reader.read().then(processResult); }); });
当然,有人可能会说ie下fetch会有问题。没错,确实会有问题,但这时候我们可以用XMLHttpRequest这个对象来实现,会更简单直接一点。
五、常见问题 1、filename通过fileapi的方式下载文件,有个很重要的问题,就是文件名。最初的一些下载方式,都是浏览器自己通过判断content-disposition这个字段来读取文件名。那现在不一样了,我们需要自己来读取文件名,这时候难免要自己读这个header,通过正则匹配下文件名。
2、cors关于cors问题,其实只要是异步请求,都会碰到。新版浏览器,我们常用access-control-allow-origin这个字段来解决跨域问题。这时候,我们在读文件名的可能要留一点,记得在header里加上Access-Control-Expose-Headers这个字段,不然fetch是取不到filename信息的。具体可以看看这篇doc
3、大文件下载问题在用fileapi的时候,我们发现文件过大会让浏览器崩溃,会导致文件下载失败。目前我在测试500mb以上的文件的时候就会碰到这样的情况。这个问题,可以通过webkitrequestfilesystem这个对象来曲线解决,但这并不是一个standard api,目前只有新版chrome支持这个对象,所以尽量不要去用。
我们推荐在拿到response的时候,读取一下blob的size,如果发现太大,就进行降级处理,使用我们最初的那中方式。
五、总结我一直相信no silver bullet这句话,虽然fileapi这种方式能解决部分问题,但其实也有很多缺点,相信大家会在实际场景中会更深刻的感受到。所以在设计组件的时候,我们在做好优雅降级的方案同时,还特意为大家开放了各种下载方式,以适应各种场景。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/79439.html
阅读 997·2023-04-25 19:35
阅读 2640·2021-11-22 09:34
阅读 3683·2021-10-09 09:44
阅读 1716·2021-09-22 15:25
阅读 2935·2019-08-29 14:00
阅读 3374·2019-08-29 11:01
阅读 2597·2019-08-26 13:26
阅读 1735·2019-08-23 18:08