前端实现有校验的大文件下载方案对比

项目需要实现下载大文件(最大1GB),并且下载时需要校验发起下载的用户是否有权限(最开始是token放在请求头这样做校验),所以想了以下这些方案。

1.blob生成url

既然要校验身份(token放请求头),就只能自己发起这个请求,用blob接收,然后生成url,再赋值给a标签的href属性,触发click事件,就会下载了。

需要注意响应头content-disposition的设置,设置为"attachment;xxx"才会下载,否则浏览器会先尝试打开/预览文件。或者给a标签设置download属性,值为文件名(如果不知道文件名可以设置download为空字符串,浏览器会尝试从响应头的content-disposition取文件名),这样浏览器也会直接下载而不是尝试打开文件。

但是这样肯定是有问题的:

  1. 大文件下载时响应很慢。我们是vue项目,http请求用axios发起,请求的then会在所有响应体都接收完成后才执行,意味着需要等到所有流接收完毕才会做下载,用户才会在页面上看到下载进程;
  2. 浏览器容易崩溃。执行到axios请求的then方法时,需要用blob变量接收,这些变量都是暂存在内存中的,而浏览器一般占用到1-2GB就会崩溃。

所以此方案打咩。

2.使用streamsaver.js

因为文件太大,自然会想到用流式下载的方式,避免占用过多内存。streamsaver.js就是实现前端流式下载的依赖包,可以解决下载大文件问题。 streamsaver原理可见:

图片来自:Http Fetch+StreamSaver.js分片下载大文件 - keitsi - 博客园

在开发时发现有一点小问题:因为项目运行环境无法访问github,而streamSaver的中间人mitm.html文件放在了github上,所以需要下载这个mitm.html文件放到项目public目录下,并修改引用地址。另外sw.js也需单独下载下来放到项目中。

解决后发现本地运行可行,浏览器不会崩溃啦!

但!是!部署后下载失败了! 现象是浏览器会新打开地址栏是xxx/mitm.html的窗口,并在控制台看到浏览器在不断地发起ping请求。(这里忘记截图了,自行想象)

发现是因为streamsaver使用了service worker,这个仅支持https或localhost,而我们部署是http,于是放弃此方案。

下图是支持的情况,如果不支持,在控制台输入ServiceWorker会报undefined错误。

3.使用file system Web API

是可以操作本地文件系统的API,但是研究了一番发现也是只支持https,放弃。

(不过这个没有仔细研究,不知道能否用这套API实现前端下载)

4.先校验再生成一次性下载链接

没辙了,还是从浏览器下载出发,想想咋做校验。最后想了这套方案:

  1. 用户点击下载后,先发起一个校验权限和身份的请求,如果通过校验,返回一个一次性的下载链接;
  2. window.open这个链接或者把这个链接给a标签并触发click。

一开始我们使用的是a标签的方式,但是如果请求的http status不是200、content-dispostion是json(就是请求失败了),浏览器还是会把这个错误响应信息下载为文件(我们用的是某个浏览器,不知道其他浏览器是不是聪明点)。

改为window.open方式的话,会新开一个标签页,响应成功了就会开始下载,并自动关闭这个新开的标签页。如果失败了,会把失败信息显示在页面。 不过还是出现了一些问题,因为后端接收到那个一次性下载请求后,需要对文件做很多处理,时间花费巨大,越大的文件越久,进而导致:

  1. 打开新页面后没有任何提示,需要等待很久才会显示下载;
  2. 经常失败,越大的文件越容易失败,而且现象居然是显示下载链接已过期,但是浏览器只显示了一个请求,后端查看日志却发现有两个请求。查看nginx错误日志发现了这样的报错: "connect() failed (111: Connection refused) while connecting to upstream, client: xxxxx, server: xxx, request: xxxx, upstream: xxx, host: xxx",意思是Nginx 尝试与 upstream(后端)建立 TCP 连接时,被目标机器立即拒绝,这条日志说明 Nginx 遇到了"网络层"错误,并可能触发 Nginx 向后端重试(注意:重试只在 Nginx→后端之间,浏览器仍只看到一个请求/响应)

第一个问题没办法缩短文件处理时间,只能把时间放到校验身份那个请求里。于是改为:

  1. 如果后端校验权限和身份成功,马上就开始处理文件,处理完成后才返回一次性下载连接,同时前端将这个请求的超时时间由原来的1.5分钟设置为半小时;
  2. 为了提升用户体验,制作了一个带有loading动画的等待下载页面,在用户点击下载后立马打开一个这个页面,等到响应一次性下载链接后,把这个链接赋值给地址栏,然后再做下载;
  3. 后端排查发现因为网关超时时间设置过小,才出现上述第2点问题,调大后就正常了。
相关推荐
咖啡星人k10 分钟前
从需求到交付:我用MonkeyCode的AI Agent完成了一个React数据看板
前端·人工智能·react.js·monkeycode
sxlishaobin12 分钟前
linux 自动清除日志 脚本
linux·服务器·前端
ZC跨境爬虫22 分钟前
跟着 MDN 学CSS day_37:(从文档流到粘性定位的底层原理)
前端·javascript·css·ui·html
IccBoY39 分钟前
NVM超详细全解教程:解决Node版本冲突(Win/Mac/Linux安装+使用+踩坑合集)
前端·node.js
wuhen_n41 分钟前
前端工程师进阶提示词工程实战
前端·langchain·ai编程
GISer_Jing1 小时前
Claude Code MCP Server 集成全解析
前端·人工智能·ai·架构
蚰蜒螟1 小时前
走进 Linux 内核:从 touch 命令到磁盘 inode 的完整旅程
java·linux·前端
因_崔斯汀1 小时前
用 AI 编程助手从零生成 3D 智慧校园数据大屏 —— Claude Code 实战全记录
前端
前端Hardy1 小时前
CSS 动画真的比 JS 快?Josh Comeau 做了组实验,结果跟直觉不一样
前端·javascript·后端
自进化Agent智能体1 小时前
MCP与Hooks:让AI Agent安全连接一切的治理框架
前端