欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

AJAX請求上傳下載進度監(jiān)控實現(xiàn)方式

 更新時間:2025年04月08日 15:45:27   作者:Micro麥可樂  
在日常 Web 開發(fā)中,AJAX(Asynchronous JavaScript and XML)被廣泛用于異步請求數(shù)據(jù),而無需刷新整個頁面,這篇文章主要介紹了AJAX請求上傳下載進度監(jiān)控指南,需要的朋友可以參考下

1. 前言

在日常 Web 開發(fā)中,AJAX(Asynchronous JavaScript and XML)被廣泛用于異步請求數(shù)據(jù),而無需刷新整個頁面。然而,當(dāng)涉及到上傳下載文件或執(zhí)行長時間運行的任務(wù)時,為了提升用戶體驗通常我們需要顯示執(zhí)行的進度條,那么監(jiān)控請求的進度就變得尤為重要。

這里博主給大家講解 XMLHttpRequestFetch API 以及 Axios封裝 在進度監(jiān)控上不同的實現(xiàn)方式

進度監(jiān)控的核心場景 :
1. 大文件上傳/下載
2. 實時數(shù)據(jù)傳輸(如視頻流)
3. 長耗時API請求
4. 用戶交互反饋優(yōu)化

2. 基于XMLHttpRequest的進度監(jiān)控

JavaScript 中,XMLHttpRequest 提供了 progress 事件,允許我們監(jiān)聽請求的進度。

2.1 基礎(chǔ)版文件上傳監(jiān)控

<input type="file" id="fileInput">
<progress id="uploadProgress" value="0" max="100"></progress>
<script>
document.getElementById('fileInput').addEventListener('change', function(e) {
  const file = e.target.files[0];
  if (!file) return;
  const xhr = new XMLHttpRequest();
  const progressBar = document.getElementById('uploadProgress');
  xhr.upload.addEventListener('progress', (e) => {
    if (e.lengthComputable) {
      const percent = (e.loaded / e.total) * 100;
      progressBar.value = percent;
      console.log(`上傳進度: ${percent.toFixed(1)}%`);
    }
  });
  xhr.addEventListener('load', () => {
    console.log('上傳完成');
  });
  xhr.open('POST', '/upload', true);
  xhr.send(file);
});
</script>

2.2 增強版多事件監(jiān)控

我們還可以集合 progress 進行多事件監(jiān)控,如下代碼:

function uploadWithProgress(file) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    // 上傳進度監(jiān)控
    xhr.upload.addEventListener('progress', (e) => {
      handleProgress('upload', e);
    });
    // 下載進度監(jiān)控(當(dāng)服務(wù)器返回大數(shù)據(jù)時)
    xhr.addEventListener('progress', (e) => {
      handleProgress('download', e);
    });
    xhr.addEventListener('error', reject);
    xhr.addEventListener('abort', reject);
    xhr.addEventListener('load', () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(xhr.response);
      } else {
        reject(xhr.statusText);
      }
    });
    xhr.open('POST', '/upload');
    xhr.setRequestHeader('Content-Type', 'application/octet-stream');
    xhr.send(file);
    function handleProgress(type, e) {
      if (e.lengthComputable) {
        const percent = (e.loaded / e.total) * 100;
        console.log(`${type} progress: ${percent.toFixed(1)}%`);
      }
    }
  });
}

3. 基于Fetch API 的進度監(jiān)控

3.1 Fetch API + ReadableStream實現(xiàn)下載監(jiān)控

Fetch API 本身沒有直接提供進度事件,但我們可以利用 ReadableStream 對響應(yīng)體進行分段讀取,從而計算已加載的字節(jié)數(shù)。

當(dāng)?shù)谝淮握埱箧溄?await fetch(url) 的時候通過獲取 headersContent-Length 返回的請求資源總大小,再結(jié)合 response.body.getReader() 來讀取 body 內(nèi)容來實現(xiàn)!

具體參考代碼如下,小伙伴可以根據(jù)自身需求進行調(diào)整:

async function downloadLargeFile(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  const contentLength = +response.headers.get('Content-Length');
  let receivedLength = 0;
  const chunks = [];
  while(true) {
    const {done, value} = await reader.read();
    if (done) break;
    chunks.push(value);
    receivedLength += value.length;
    const percent = (receivedLength / contentLength) * 100;
    console.log(`下載進度: ${percent.toFixed(1)}%`);
  }
  const blob = new Blob(chunks);
  return blob;
}
// 使用示例
downloadLargeFile('/large-file.zip')
  .then(blob => {
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'file.zip';
    a.click();
  });

3.2 Fetch API 上傳進度監(jiān)控(偽方案)

Fetch API 原生并不支持上傳進度監(jiān)控。不過可以采用將文件包裝成一個自定義的 ReadableStream 來實現(xiàn) “偽”上傳進度監(jiān)控 。
需要注意的是,由于各瀏覽器對 Request 流處理的支持程度不一,該方法可能并非在所有環(huán)境下都能穩(wěn)定工作。博主建議非必要不要采用這種方式

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Fetch API 上傳進度監(jiān)控</title>
</head>
<body>
  <input type="file" id="fileInput">
  <button onclick="uploadFile()">上傳文件</button>
  <progress id="progressBar" value="0" max="100"></progress>
  <span id="progressText">0%</span>
  <script>
    function uploadFile() {
      const fileInput = document.getElementById('fileInput');
      const file = fileInput.files[0];
      if (!file) {
        alert('請選擇文件');
        return;
      }
      const progressBar = document.getElementById('progressBar');
      const progressText = document.getElementById('progressText');
      const total = file.size;
      let uploaded = 0;
      // 構(gòu)造一個自定義的 ReadableStream 來包裝文件流
      const stream = new ReadableStream({
        start(controller) {
          const reader = file.stream().getReader();
          function push() {
            reader.read().then(({ done, value }) => {
              if (done) {
                controller.close();
                return;
              }
              uploaded += value.byteLength;
              const percent = (uploaded / total) * 100;
              progressBar.value = percent;
              progressText.innerText = percent.toFixed(2) + '%';
              controller.enqueue(value);
              push();
            }).catch(error => {
              console.error('讀取文件錯誤:', error);
              controller.error(error);
            });
          }
          push();
        }
      });
      // 使用 Fetch API 發(fā)送 POST 請求
      fetch('/upload', {
        method: 'POST',
        headers: {
          // 根據(jù)后端要求設(shè)置合適的 Content-Type
          // 注意:如果使用 FormData 上傳文件,瀏覽器會自動設(shè)置 multipart/form-data
          'Content-Type': 'application/octet-stream'
        },
        body: stream
      })
      .then(response => response.json())
      .then(data => {
        console.log('上傳成功:', data);
        alert('上傳完成');
      })
      .catch(error => {
        console.error('上傳失敗:', error);
        alert('上傳失敗');
      });
    }
  </script>
</body>
</html>

注意事項

  • 上傳進度:Fetch API 本身不提供上傳進度事件,上述方法通過包裝文件流來模擬上傳進度,但并非所有瀏覽器都支持這種方式,穩(wěn)定性可能不如 XMLHttpRequest
  • 內(nèi)容類型:如果后端要求 multipart/form-data 格式,建議仍采用 XMLHttpRequest 或使用 FormData 對象,因為自定義流方式上傳數(shù)據(jù)可能需要后端特殊處理

4. Axios封裝進度監(jiān)控方案

通過封裝 Axios 請求,可以同時監(jiān)聽上傳和下載的進度,提升用戶體驗,再次之前我們先來看看未封裝前最原始的上傳和下載是如何實現(xiàn)的~

4.1 Axios 上傳進度監(jiān)控

Axios 支持通過配置項 onUploadProgress 來監(jiān)聽上傳進度。以下示例展示了如何使用 Axios 上傳文件,并在頁面上顯示進度信息:

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>Axios 上傳進度監(jiān)控</title>
</head>
<body>
  <h2>文件上傳(Axios)</h2>
  <input type="file" id="fileInput">
  <button onclick="uploadFile()">上傳文件</button>
  <br>
  <progress id="uploadProgress" value="0" max="100" style="width: 300px;"></progress>
  <span id="uploadText">0%</span>
  <!-- 引入 Axios 庫 -->
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    function uploadFile() {
      const fileInput = document.getElementById('fileInput');
      const file = fileInput.files[0];
      if (!file) {
        alert('請選擇文件');
        return;
      }
      const formData = new FormData();
      formData.append('file', file);
      axios.post('/upload', formData, {
        headers: {
          'Content-Type': 'multipart/form-data'
        },
        onUploadProgress: function (progressEvent) {
          if (progressEvent.lengthComputable) {
            const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
            document.getElementById('uploadProgress').value = percent;
            document.getElementById('uploadText').innerText = percent + '%';
          }
        }
      })
      .then(response => {
        alert('上傳成功');
        console.log(response.data);
      })
      .catch(error => {
        alert('上傳失敗');
        console.error(error);
      });
    }
  </script>
</body>
</html>

代碼解釋:
使用 FormData 對象包裝上傳的文件;
通過 Axios 的 onUploadProgress 事件監(jiān)聽上傳過程,并實時更新進度條和百分比顯示。

4.2 Axios 下載進度監(jiān)控

同理,Axios 還支持 onDownloadProgress 事件來監(jiān)控文件下載進度。下面的示例展示了如何通過 Axios 下載文件并實時顯示下載進度:

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>Axios 下載進度監(jiān)控</title>
</head>
<body>
  <h2>文件下載(Axios)</h2>
  <button onclick="downloadFile()">下載文件</button>
  <br>
  <progress id="downloadProgress" value="0" max="100" style="width: 300px;"></progress>
  <span id="downloadText">0%</span>
  <!-- 引入 Axios 庫 -->
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    function downloadFile() {
      axios.get('/download', {
        responseType: 'blob',  // 指定響應(yīng)數(shù)據(jù)類型為 Blob
        onDownloadProgress: function (progressEvent) {
          if (progressEvent.lengthComputable) {
            const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
            document.getElementById('downloadProgress').value = percent;
            document.getElementById('downloadText').innerText = percent + '%';
          }
        }
      })
      .then(response => {
        // 創(chuàng)建一個臨時鏈接下載文件
        const url = window.URL.createObjectURL(new Blob([response.data]));
        const a = document.createElement('a');
        a.href = url;
        a.download = 'downloaded_file';
        document.body.appendChild(a);
        a.click();
        a.remove();
        window.URL.revokeObjectURL(url);
      })
      .catch(error => {
        alert('下載失敗');
        console.error(error);
      });
    }
  </script>
</body>
</html>

代碼解釋:
1、我們通過 axios.get 請求下載文件,并設(shè)置 responseType 為 blob;
2、通過 onDownloadProgress 事件監(jiān)聽下載進度,并更新進度條;
3、下載完成后利用 Blob 和臨時鏈接觸發(fā)瀏覽器下載文件。

4.3 封裝 Axios 實例

為了在項目中更方便地使用進度監(jiān)控功能,可以將 Axios 進行封裝。例如,我們可以創(chuàng)建一個 Axios 實例,并在請求配置中統(tǒng)一處理上傳和下載進度

import axios from 'axios';
// 請求配置
const axiosInstance = axios.create({
  onUploadProgress: progressEvent => {
    const percent = Math.round(
      (progressEvent.loaded * 100) / progressEvent.total
    );
    console.log(`上傳進度: ${percent}%`);
  },
  onDownloadProgress: progressEvent => {
    const percent = Math.round(
      (progressEvent.loaded * 100) / progressEvent.total
    );
    console.log(`下載進度: ${percent}%`);
  }
});
// 封裝上傳方法
async function axiosUpload(file) {
  const formData = new FormData();
  formData.append('file', file);
  try {
    const response = await axiosInstance.post('/upload', formData, {
      headers: {'Content-Type': 'multipart/form-data'}
    });
    return response.data;
  } catch (error) {
    console.error('上傳失敗:', error);
    throw error;
  }
}

使用時,在頁面中調(diào)用封裝方法即可,這樣通過封裝后的 Axios 實例,我們可以在項目中更加方便地復(fù)用進度監(jiān)控功能。

5. 特殊場景處理技巧

通常在一些特殊場景下,針對進度監(jiān)控我們還需要一些處理技巧,這里博主分享兩個分別是 分塊上傳監(jiān)控帶寬計算與預(yù)估

5. 1 分塊上傳監(jiān)控

async function chunkedUpload(file, chunkSize = 1024 * 1024) {
  const chunks = Math.ceil(file.size / chunkSize);
  let uploadedChunks = 0;
  for (let i = 0; i < chunks; i++) {
    const start = i * chunkSize;
    const end = Math.min(start + chunkSize, file.size);
    const chunk = file.slice(start, end);
    await axios.post('/upload-chunk', chunk, {
      headers: {'Content-Range': `bytes ${start}-${end-1}/${file.size}`},
      onUploadProgress: e => {
        const chunkPercent = (e.loaded / e.total) * 100;
        const totalPercent = ((uploadedChunks + e.loaded) / file.size) * 100;
        console.log(`分塊進度: ${chunkPercent.toFixed(1)}%`);
        console.log(`總進度: ${totalPercent.toFixed(1)}%`);
      }
    });
    uploadedChunks += chunk.size;
  }
}

5. 2 帶寬計算與預(yù)估

let startTime;
let lastLoaded = 0;
xhr.upload.addEventListener('progress', e => {
  if (!startTime) startTime = Date.now();
  const currentTime = Date.now();
  const elapsed = (currentTime - startTime) / 1000; // 秒
  const speed = e.loaded / elapsed; // bytes/s
  const remaining = (e.total - e.loaded) / speed; // 剩余時間
  console.log(`傳輸速度: ${formatBytes(speed)}/s`);
  console.log(`預(yù)計剩余時間: ${remaining.toFixed(1)}秒`);
});
function formatBytes(bytes) {
  const units = ['B', 'KB', 'MB', 'GB'];
  let unitIndex = 0;
  while (bytes >= 1024 && unitIndex < units.length - 1) {
    bytes /= 1024;
    unitIndex++;
  }
  return `${bytes.toFixed(1)} ${units[unitIndex]}`;
}

6. 結(jié)語

本篇文章介紹了如何在前端請求中監(jiān)控上傳和下載進度,并提供了完整的前端和后端代碼示例。通過合理運用這些技術(shù),開發(fā)者可以構(gòu)建出具有專業(yè)級進度反饋的Web應(yīng)用,顯著提升用戶在處理大文件傳輸時的體驗。

希望能幫助小伙伴們在項目中更好地處理大文件傳輸,提高用戶體驗!

到此這篇關(guān)于AJAX請求上傳下載進度監(jiān)控指南的文章就介紹到這了,更多相關(guān)AJAX上傳下載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論