封装bootstrap的Toasts组件实现的多个下载任务弹框
最近要改一个下载任务的需求,原来的代码要么使用ajax异步请求看不到下载进度,要么使用window.open(url,‘__blank’)打开一个新页面既看不到下载进度也要手动关闭新打开的窗口,于是决定自己封装一个下载任务的进度条提示框。由于前端框架使用的bootstrap,还是使用Toasts组件来提示吧。能提示下载进度的前提是可以获得要下载文件的大小,服务器端使用响应头Content-length返回文件大小
<link href="./html-demos/plugins/bootstrap-5.1.3-dist/css/bootstrap.min.css" rel="stylesheet" /> <script src="./html-demos/plugins/bootstrap-5.1.3-dist/js/bootstrap.bundle.js"></script> <button type="button" class="btn btn-outline-primary mt-3 ms-3" onclick="downloadTask.show()"> Download Task <span class="badge bg-danger"> 0 </span> </button> <div class="toast-container position-fixed top-0 end-0 mt-3 me-3 p-2"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" id="liveToast"> <div class="toast-header">Download Task</div> <div class="toast-body"> <div id="liveTask" style="display: none" class="mb-2"> <div style="height: 1.5rem"> <strong class="me-auto filename"></strong> <small class="text-muted progress-num"></small> </div> <div class="progress" role="progressbar" aria-label="Basic example" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"> <div class="progress-bar" style="width: 0%"></div> </div> </div> </div> </div> </div> <script> const downloadTask = { begin: 0, num: 0, taskList: {}, toast: null, init: function (id) { this.toast = new bootstrap.Toast(document.getElementById(id)); }, show: function () { if (this.toast) this.toast.show(); }, create: function () { this.begin += 1; this.num += 1; const key = "liveTask" + this.begin; const newTask = document.getElementById("liveTask").cloneNode(true); newTask.setAttribute("id", key); newTask.style.display = "block"; newTask.querySelector(".filename").innerText = "processing..."; document.querySelector(".toast-body").appendChild(newTask); this.taskList[key] = key; const badgeEle = document.querySelector(".badge"); badgeEle.innerText = this.num; badgeEle.style.display = "inline"; return key; }, remove: function (key) { delete this.taskList[key]; document.getElementById(key).remove(); }, download: function (url) { this.show(); const newTaskId = this.create(); const filenameEle = document.getElementById(newTaskId).querySelector(".filename"); const progressNumEle = document.getElementById(newTaskId).querySelector(".progress-num"); const downloadProgressBarEle = document.getElementById(newTaskId).querySelector(".progress-bar"); const badgeEle = document.querySelector(".badge"); const this_obj = this; let xhr = new XMLHttpRequest(); xhr.responseType = "blob"; xhr.open("GET", url, true); xhr.addEventListener("progress", function (event) { if (event.total) { const progress = (event.loaded / event.total) * 100; console.log(progress); downloadProgressBarEle.style.width = progress + "%"; progressNumEle.innerText = progress + "%"; if (progress == 100) { this_obj.num -= 1; badgeEle.innerText = this_obj.num; if (this_obj.num < 1) badgeEle.style.display = "none"; setTimeout(() => { this_obj.remove(newTaskId); }, 2000); } } }); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var link = document.createElement("a"); link.href = window.URL.createObjectURL(new Blob([xhr.response])); const filename = this_obj.getFileName(xhr.getResponseHeader("content-disposition")); link.download = filename; link.click(); var headers = xhr.getAllResponseHeaders().toLowerCase(); filenameEle.innerText = filename; } }; xhr.ontimeout = function (event) { alert("请求超时!"); }; xhr.send(); }, getFileName: function (str) { //兼容中文文件名 let name = this.handleFileName(str, "filename*=utf-8''", true); if (name == "") name = this.handleFileName(str, "filename="); return name; }, handleFileName: function (str, file_str, decode = false) { let start = str.indexOf(file_str); let filename = ""; let end = 0; if (start != -1) { end = str.indexOf(";", start); if (end == -1) { filename = str.substring(start, str.length); } else { filename = str.substring(start, end); } if (decode) { return decodeURI(filename.substring(file_str.length)); } else { return filename.substring(file_str.length); } } return ""; }, }; downloadTask.init("liveToast"); downloadTask.download("./test1.php"); downloadTask.download("./test1.php"); setTimeout(() => { downloadTask.download("./test1.php"); }, 4000); setTimeout(() => { downloadTask.download("./test1.php"); }, 10000); </script>
即可实现下面的效果:
分类:
javascript
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix