bootstrap下载进度条
原来的web应用中文件下载都是打开一个新的窗口,文件自动下载后需手动关闭新打开的窗口,且没有下载进度显示。
<script> window.open(downloadUrl,'__blank'); </script>
利用bootstrap和Toasts和Progress组件写一个可以显示下载进度的弹出框,下载完成自己关闭。
用本地文件模拟下载,服务器端php代码
//本地文件 $file = 'zz.csv'; header("Content-type: " . filetype($file)); header("Content-length: " . filesize($file)); header("Content-Type:application/octet-stream"); header("Content-Disposition: attachment;filename=" . $file); header("Content-Transfer-Encoding: binary"); header('Pragma: no-cache'); header('Expires: 0'); echo file_get_contents($file); exit;
bootstrap的Toasts封装,这里使用Toasts组件堆叠显示
<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> <div class="toast-container position-fixed top-0 end-0"> <div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false" id="liveToast"> <div class="toast-header"> <strong class="me-auto filename"></strong> <small class="text-muted progress-num"></small> </div> <div class="toast-body"> <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> <script> const toast = { begin: 0, list: {}, create: function () { this.begin += 1; const key = "liveToast" + this.begin; const newToast = document.getElementById("liveToast").cloneNode(true); newToast.setAttribute("id", key); document.querySelector(".toast-container").appendChild(newToast); this.list[key] = new bootstrap.Toast(document.getElementById(key)); return key; }, remove: function (key) { this.list[key].hide(); delete this.list[key]; document.getElementById(key).remove(); }, }; </script>
使用XMLHttpRequest请求后端地址下载
<script> function downloadProgress(url) { const newToastId = toast.create(); const filenameEle = document.getElementById(newToastId).querySelector(".filename"); const progressNumEle = document.getElementById(newToastId).querySelector(".progress-num"); const downloadProgressBarEle = document.getElementById(newToastId).querySelector(".progress-bar"); toast.list[newToastId].show(); let xhr = new XMLHttpRequest(); xhr.timeout = 3000; xhr.responseType = "blob"; xhr.open("GET", url, true); xhr.addEventListener("progress", function (event) { if (event.total) { const progress = (event.loaded / event.total) * 100; downloadProgressBarEle.style.width = progress + "%"; progressNumEle.innerText = progress + "%"; if (progress == 100) { setTimeout(() => { toast.remove(newToastId); }, 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(); filenameEle.innerText = filename; } }; xhr.ontimeout = function (event) { alert("请求超时!"); }; xhr.send(); } downloadProgress("./test.php"); </script>
经测试,可以正常显示弹出框、进度条和下载文件。
将服务器端代码换成PhpSpreadsheet包生成的文件,看下是否可以正常下载:
require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; $spreadSheet = new Spreadsheet(); $sheet = $spreadSheet->getActiveSheet(); for ($i = 0; $i < 20000; $i++) { $sheet->setCellValueByColumnAndRow(1, $i, $i); $sheet->setCellValueByColumnAndRow(2, $i, "hello"); $sheet->setCellValueByColumnAndRow(3, $i, 555); } header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename=simple.xlsx'); header('Cache-Control: max-age=0'); header('Cache-Control: cache, must-revalidate'); // HTTP/1.1 $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadSheet, 'Xlsx'); $writer->save('php://output'); exit;
发现响应头没有Content-length
使用ob函数修改导出部分的代码
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadSheet, 'Xlsx'); ob_start(); $writer->save('php://output'); $file_size = ob_get_length(); header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename=simple.xlsx'); header('Cache-Control: max-age=0'); header('Cache-Control: cache, must-revalidate'); header("Content-length: " . $file_size); ob_get_flush(); exit;
测试结果:页面右上角显示这样的下载进度弹出框,下载完成2s后关闭弹出框
使用bootstrap的Toasts弹出框可以同时出现多个弹出框,堆叠显示,可用于同时多个下载任务
若同一个时间只会有一个下载任务,也可以使用sweetalert2和bootstrap的Progress组件组合使用
<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> <link href="./html-demos/plugins/sweetalert2/sweetalert2.min.css" rel="stylesheet" /> <script src="./html-demos/plugins/sweetalert2/sweetalert2.all.min.js"></script> <script> function downloadProgress(url) { const Toast = Swal.mixin({ toast: true, position: "top-start", showConfirmButton: false, }); Toast.fire({ icon: false, title: `<div class="d-flex"> <span class="filename"></span> <span class="d-inline-block ms-auto progress-num" style="width: 4rem"></span> </div>`, html: `<div class="progress" role="progressbar" aria-label="Basic example" aria-valuenow="1" aria-valuemin="0" aria-valuemax="100"> <div class="progress-bar" style="width: 1%"></div> </div>`, }); const filenameEle = Toast.getTitle().querySelector(".filename"); const progressNumEle = Toast.getTitle().querySelector(".progress-num"); const downloadProgressBarEle = Toast.getHtmlContainer().querySelector(".progress-bar"); let xhr = new XMLHttpRequest(); xhr.timeout = 3000; xhr.responseType = "blob"; xhr.open("GET", url, true); xhr.addEventListener("progress", function (event) { console.log(event); if (event.total) { const progress = (event.loaded / event.total) * 100; downloadProgressBarEle.style.width = progress + "%"; progressNumEle.innerText = progress + "%"; if (progress == 100) { setTimeout(() => { Toast.close(); }, 2000); } } }); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { console.log(xhr); 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(); filenameEle.innerText = filename; } }; xhr.ontimeout = function (event) { alert("请求超时!"); }; xhr.send(); } downloadProgress("./test1.php"); </script>
网络请求使用axios时:
axios.get(url, {
responseType: "blob",
onDownloadProgress: function (progressEvent) {
....
},
})
.then((response) => {
...
});
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix