如何在浏览器中使用Worker线程显示下载进度?

新手上路,请多包涵

使用worker线程异步下载项目中的附件,选择100个,下载成100个文件的需求。(目前最多同时下载10个)在modelAndView跳转的对应的jsp中,然后new worker请求下载附件,在worker使用XMLHttpRequest请求下载。然后再worker的监听事件中使用a标签下载附件。
这存在一个问题,只会在下载完成时候的时候浏览器弹出附件下载框,不显示下载进度信息。
怎么显示下载进度信息?示例代码如下:
worker.onmessage = function(){
let blobUrl = new Blob([res.data], {type: 'application/zip'})
let a = document.createElement('a');
let url = window.URL.createObjectURL(blobUrl);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
}

希望和在浏览器中出现下载进度。

阅读 1k
avatarAI BotBETA

要在浏览器中使用 Web Worker 线程显示下载进度,你需要做几件事情:

  1. 修改 Worker 以返回下载进度:你需要让 Worker 定期发送消息回主线程,报告下载的进度。
  2. 在主线程中更新进度:根据 Worker 发送的进度信息,更新 UI 显示下载进度。

下面是一个修改后的示例,展示如何显示下载进度。这个示例包括在主线程和 Worker 线程之间的通信。

主线程代码(JSP 页面中的 JavaScript)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Download with Progress</title>
</head>
<body>
    <div id="progress-container">
        <div id="progress-bar" style="width: 0%; background-color: green; text-align: center; color: white; padding: 1px;">0%</div>
    </div>

    <script>
        const worker = new Worker('downloadWorker.js');

        // 发送下载任务给 Worker
        const urls = [/* 这里是100个附件的URL数组 */];
        const batchSize = 10;  // 同时下载的最大数量
        let currentIndex = 0;

        function startDownloads() {
            for (let i = 0; i < batchSize && currentIndex < urls.length; i++) {
                worker.postMessage({ url: urls[currentIndex], index: currentIndex });
                currentIndex++;
            }
        }

        worker.onmessage = function(event) {
            const { index, progress, data, fileName } = event.data;
            if (progress !== undefined) {
                const totalDownloads = urls.length;
                const overallProgress = ((index + 1) * batchSize + progress) / (totalDownloads * 100);
                const progressBar = document.getElementById('progress-bar');
                progressBar.style.width = `${Math.min(overallProgress * 100, 100)}%`;
                progressBar.textContent = `${Math.min(overallProgress * 100, 100).toFixed(2)}%`;
            } else if (data && fileName) {
                const blobUrl = URL.createObjectURL(new Blob([data], { type: 'application/zip' }));
                const a = document.createElement('a');
                a.href = blobUrl;
                a.download = fileName;
                a.click();
                URL.revokeObjectURL(blobUrl);

                // 如果有更多文件需要下载,继续下载
                if (currentIndex < urls.length) {
                    startDownloads();
                }
            }
        };

        // 开始下载
        startDownloads();
    </script>
</body>
</html>

Worker 线程代码(downloadWorker.js)

self.onmessage = function(event) {
    const { url, index } = event.data;
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'blob';

    xhr.onprogress = function(e) {
        if (e.lengthComputable) {
            const progress = Math.round((e.loaded * 100) / e.total);
            self.postMessage({ index, progress });
        }
    };

    xhr.onload = function() {
        if (xhr.status === 200) {
            self.postMessage({ index, data: xhr.response, fileName: `file_${index}.zip` });
        } else {
            console.error(`Failed to download file ${index}: ${xhr.statusText}`);
        }
    };

    xhr.onerror = function() {
        console.error(`Error downloading file ${index}`);
    };

    xhr.send();
};

解释

  1. 主线程

    • 创建一个进度条容器。
    • 使用 Web Worker 发送下载任务。
    • 监听 Worker 发送的消息,更新进度条。
    • 下载完成后,创建 Blob URL 并模拟点击 <a> 标签下载文件。
    • 如果还有文件需要下载,继续发送下载任务。
  2. Worker 线程

    • 接收下载任务(URL 和索引)。
    • 使用 XMLHttpRequest 发起下载请求。
    • onprogress 事件中,计算并发送下载进度给主线程。
    • onload 事件中,发送下载的数据和文件名给主线程。

这样,你就可以在浏览器中显示下载进度了。

1 个回答

效果如下:http://43.143.65.230/demo/index.html

下载文件进度条worker

// downloadWorker.js
self.addEventListener('message', async (event) => {
  const { url } = event.data; // 从主线程接收下载 URL

  try {
    const response = await fetch(url);

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const contentLength = +response.headers.get('Content-Length'); // 获取文件总大小
    const reader = response.body.getReader();
    let receivedLength = 0;

    while (true) {
      const { done, value } = await reader.read();

      if (done) {
        self.postMessage({ type: 'complete' }); // 下载完成
        break;
      }

      receivedLength += value.length;
      const progress = (receivedLength / contentLength) * 100; // 计算进度
      self.postMessage({ type: 'progress', progress }); // 发送进度
    }
  } catch (error) {
    self.postMessage({ type: 'error', error: error.message }); // 发送错误
  }
});

demo

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Download with Worker</title>
  </head>
  <body>
    <h1>Download Progress</h1>
    <progress id="download-progress" value="0" max="100"></progress>
    <p id="status"></p>

    <script>
      const worker = new Worker('downloadWorker.js'); // 初始化 Worker
      const progressBar = document.getElementById('download-progress');
      const statusText = document.getElementById('status');

      // 监听 Worker 发送的消息
      worker.addEventListener('message', (event) => {
        const { type, progress, error } = event.data;

        switch (type) {
          case 'progress':
            progressBar.value = progress; // 更新进度条
            statusText.textContent = `Downloading... ${progress.toFixed(2)}%`;
            break;
          case 'complete':
            statusText.textContent = 'Download complete!';
            console.log(event);
            break;
          case 'error':
            statusText.textContent = `Error: ${error}`;
            break;
        }
      });

      // 启动下载任务
      worker.postMessage({ url: 'https://cdn.zsdx.cn/website-2024/home/offfine-video4.mp4' });
    </script>
  </body>
</html>

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题