当前位置:首页 > 探索

web前端下载

干前端这行,端下谁没写过几行下载代码?端下但真要让你从零实现一个靠谱的下载功能,坑比想象中多得多。端下我见过太多人上来就标签加个download属性,端下结果文件稍微大点浏览器直接卡死,端下或者后端返回个二进制流就懵了。端下

最原始的端下做法确实简单。动态创建一个anchor,端下设置href和download,端下点击,端下完事。端下适用于图片、端下PDF这些浏览器能直接打开的端下文件。但有个细节很多人不知道——download属性只在同源URL下生效。端下跨域资源?端下浏览器会忽略download,直接跳转打开。我去年做个后台项目,要从CDN拉Excel模板,就栽在这上面。后来改成后端代理转发,绕了一圈。

说到后端代理,就不得不提Blob方案。这是目前最通用的做法。前端发请求,responseType设成'blob',拿到二进制数据后转成Object URL,再挂到anchor上触发下载。代码大概长这样:

const res = await fetch(url); const blob = await res.blob(); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'filename.xlsx'; link.click(); URL.revokeObjectURL(link.href);

注意最后那行revokeObjectURL。不写的话,这个Blob会一直占着内存,页面开久了直接崩。我调试过一个线上问题,运营同事连续下载几十个报表后浏览器白屏,查了半天就是漏了这句。

大文件下载是另一个战场。几百兆的视频或者数据集,用Blob全量加载到内存?用户等半天不说,手机端直接OOM。这时候得用Streams API做分片下载,配合进度条。Chrome 89之后支持得不错,但Safari那边又是另一套脾气。我现在的策略是:超过50MB的文件,优先让后端给一个直链,前端只负责触发,别自己扛。

还有个恶心的情况——文件名。后端在Content-Disposition里塞个UTF-8编码的中文名,前端解析出来经常乱码。不同浏览器对filename*和filename的处理还不一样。我一般直接让后端在自定义header里返回Base64编码的文件名,前端解码,省心。虽然不够优雅,但管用。

下载进度条也是个伪需求重灾区。用fetch的话,拿不到upload和download的进度事件,得用XMLHttpRequest。对,就是那个被很多人嫌弃的XHR。它的onprogress事件能拿到loaded和total,算个百分比轻轻松松。所以别迷信fetch,该用XHR的时候别犹豫。

最后说个容易被忽略的点:移动端。iOS的Safari对下载的支持一直很拧巴。Blob URL在iOS上有时会直接打开文件而不是下载,用户体验极差。我的做法是检测到iOS时,引导用户长按链接或者用分享菜单保存。虽然不完美,但比白屏强。

写了这么多,其实核心就一句:下载这事,没有银弹。小文件用Blob,大文件走直链,跨域靠后端,iOS要妥协。每次遇到新场景,还是得老老实实查兼容性、写降级方案。前端这碗饭,从来都不是复制粘贴那么简单。

分享到: