首页 > 其他分享 >bootstrap下载进度条

bootstrap下载进度条

时间:2023-01-06 16:12:16浏览次数:32  
标签:const header 进度条 bootstrap filename xhr progress event 下载

原来的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) => {
           ...
      });    

 

标签:const,header,进度条,bootstrap,filename,xhr,progress,event,下载
From: https://www.cnblogs.com/caroline2016/p/17030788.html

相关文章