当前位置: 首页 > news >正文

关于前端form提交后端返回文件流触发浏览器下载(并发控制)

前文介绍过这种文件下载方式,它有好处也有不足的地方。好处是可以充分利用浏览器自身的资源调度优势,开发只管提交不用关注下载细节,用户可以在浏览器下载任务中看到下载的状态。不足之处是只能下载到浏览器配置的默认下载位置,而且JS里不能掌握下载状态。
更为关键的是如果一次下载的文件较多的话,提交了N个请求,这样浏览器会创建N个标签页,虽然浏览器自己会调度,下载完后自行关闭这些标签页,但是会带来密集恐惧,那有没有办法控制下浏览器下载的任务数(虽然处于下载中的最多6个任务,但是等待的任务也是任务啊)
既然文件下载提交后,无法从浏览器得到任务下载状态,那么从服务器呢,这个方案是可行的。
前端任务提交后,后端服务器读取本地文件流通过pipeline对接到response,response设置头参数,pipeline完成后回调sse消息发送到前端通知文件下载完成。

app.get('/events', (req,res)=>{res.writeHead(200,  { 'Content-Type': 'text/event-stream', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache' });res.write(`data: ${JSON.stringify({"msg":"消息服务正常"})}\n\n`);let clientid=stringRandom(32, { letters: 'ABCDEF' });clients.push({ "clientid":clientid, "uid":req.session.user.userid, "res":res });req.on('close', () => { clients = clients.filter(item => (item.clientid != clientid)); });
});function sendssemsg(jvar)clients.forEach(client=>{ if (client.uid==jvar.uid) { client.res.write(`data: ${JSON.stringify(jvar.msg)}\n\n`) } });
}...
app.post("/getfile",express.urlencoded({ extended: false }),(req,res)=>{
...res.set({ "Content-Type": "application/octet-stream",  "Content-Disposition": "attachment;filename* = UTF-8''"+fixedEncodeURIComponent(filename.substr(filename.lastIndexOf("/")+1)),"Content-Length": stats.size });pipeline(fs.createReadStream(filepath), res, (err) => { if (err) console.log("下载出错") else sendssemsg({"uid":userid,"msg":filename+" 下载完成"})});...

通过sse发送下载结果,效率最好
前端收到相应的sse消息,就可以处理下载任务队列操作了

	function downloadfile(filename) {let form=$("#formp");$("input[name=filename]").attr("value",filename);form.submit();}var indownloading=0;const evtSource = new EventSource("/events");evtSource.addEventListener('message', function(event) {//let jvar=JSON.parse(event.data);indownloading-=1;if (waitinglist.length>0) {indownloading+=1downloadfile(waitinglist.shift());}})let indownloading=0;let taskLimit=navigator.hardwareConcurrency;while ((indownloading<tasklimit)&&(waitinglist.length>0)) {indownloading+=1;downloadfile(waitinglist.shift());}
<form name="formp" id="formp" action="/getfile" method="post" target="_blank" rel="noopener noreferrer">
<input name="filename" value="">
</form>

这样效果就是下载任务控制在CPU核数,浏览器上看到的下载标签也不会太多,视觉观感上对用户更友好一些。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 程序员常用的10种算法
  • Pandas DataFrame 数据转换处理和多条件查询
  • 【模板】连接外围数据库
  • Java高效写入大量数据到Excel文件——使用Apache POI的SXSSFWorkbook
  • WIFI 频段及信道简介
  • 【摆脱被360安全卫士荼毒:使用这2个软件就够了】
  • GoFly快速开发后台框架当后端接口请求返回403提示码就跨域问题/请求端域名拦截问题
  • [数据集][目标检测]电力场景红外图像输电线路绝缘子检测数据集VOC+YOLO格式1846张1类别
  • 认识泛型VS包装类
  • 第5章 虚拟机的安装和使用
  • MyBatis-Plus 一、(基础应用)
  • ROS2常用指令
  • 探索ISP自动曝光技术:工作原理与应用(一)
  • IEEE802网络协议和标准
  • 固废检测算法实际应用方案固废检测算法源码解析
  • 【Linux系统编程】快速查找errno错误码信息
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • Elasticsearch 参考指南(升级前重新索引)
  • ES6系统学习----从Apollo Client看解构赋值
  • go append函数以及写入
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • javascript 总结(常用工具类的封装)
  • JAVA多线程机制解析-volatilesynchronized
  • jQuery(一)
  • Netty源码解析1-Buffer
  • php ci框架整合银盛支付
  • php中curl和soap方式请求服务超时问题
  • Promise面试题,控制异步流程
  • sublime配置文件
  • Swift 中的尾递归和蹦床
  • Swoft 源码剖析 - 代码自动更新机制
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • uva 10370 Above Average
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • Vue2.x学习三:事件处理生命周期钩子
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 多线程事务回滚
  • 技术发展面试
  • 浏览器缓存机制分析
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 删除表内多余的重复数据
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ‌分布式计算技术与复杂算法优化:‌现代数据处理的基石
  • # .NET Framework中使用命名管道进行进程间通信
  • # Apache SeaTunnel 究竟是什么?
  • #laravel部署安装报错loadFactoriesFrom是undefined method #
  • (2024最新)CentOS 7上在线安装MySQL 5.7|喂饭级教程
  • (STM32笔记)九、RCC时钟树与时钟 第一部分
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking