最近要改一个下载任务的需求,原来的代码要么使用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])); filename = xhr.getResponseHeader("content-disposition").split("=")[1]; link.download = filename; link.click(); var headers = xhr.getAllResponseHeaders().toLowerCase(); filenameEle.innerText = filename; } }; xhr.ontimeout = function (event) { alert("请求超时!"); }; xhr.send(); }, }; downloadTask.init("liveToast"); downloadTask.download("./test1.php"); downloadTask.download("./test1.php"); setTimeout(() => { downloadTask.download("./test1.php"); }, 4000); setTimeout(() => { downloadTask.download("./test1.php"); }, 10000); </script>
即可实现下面的效果:
标签:function,const,bootstrap,弹框,xhr,key,progress,Toasts,document From: https://www.cnblogs.com/caroline2016/p/17188252.html