http网络请求与下载进度

打印 上一主题 下一主题

主题 663|帖子 663|积分 1989

Http_request 目次

一、XMLHttpRequest

在使用 Fetch API 进行网络请求时,原生的 Fetch API 并不直接支持获取下载进度的功能,由于 Fetch API 重要是基于 Promise 的,它重要关注于请求的乐成或失败,以及相应数据的处置惩罚,而不直接处置惩罚像进度跟踪这样的底层细节。
不过,你可以通过一些技巧或方法间接实现下载进度的跟踪。以下是一些常用的方法:
1. 使用 XMLHttpRequest

虽然 Fetch API 较为今世,但如果你需要跟踪下载进度,XMLHttpRequest 可能是一个更好的选择。XMLHttpRequest 提供了 onprogress 事件,可以用来追踪下载进度。
  1. var xhr = new XMLHttpRequest();
  2. xhr.open('GET', 'your-file-url', true);
  3. xhr.responseType = 'blob';
  4. xhr.onprogress = function (e) {
  5.     if (e.lengthComputable) {
  6.         var percentComplete = (e.loaded / e.total) * 100;
  7.         console.log(percentComplete + '% downloaded');
  8.     }
  9. };
  10. xhr.onload = function () {
  11.     if (this.status == 200) {
  12.         // 处理响应数据
  13.     }
  14. };
  15. xhr.send();
复制代码
在上述代码中,我们创建了一个 XMLHttpRequest 对象,并设置了 onprogress 事件处置惩罚函数。在该函数中,通过判断 e.lengthComputable 是否为真,来确定是否可以计算下载进度。如果可以计算,则通过 e.loaded 和 e.total 计算出已下载的百分比,并将其打印到控制台
2.XMLHttpRequest 的进一步封装

  1.    xhrToDownload(options, onProgress, onSuccess, onError) {
  2.             return new Promise((resolve, reject) => {
  3.                 const xhr = new XMLHttpRequest();
  4.                 xhr.open(options.method || 'GET', options.url);
  5.                 xhr.responseType = options.responseType || 'blob';
  6.                 xhr.onprogress = function(e) {
  7.                     if (e.lengthComputable) {
  8.                         const progress = (e.loaded / e.total) * 100;
  9.                         onProgress && onProgress(progress);
  10.                     }
  11.                 };
  12.                 xhr.onload = function(e) {
  13.                     if (xhr.status === 200) {
  14.                         // onSuccess && onSuccess(xhr.response);
  15.                         console.log('上传成功', xhr);
  16.                         resolve({ status: xhr.status, data: xhr.response })
  17.                     } else {
  18.                         onError && onError(xhr.statusText);
  19.                         reject({ status: xhr.status, data: xhr.statusText }); // 拒绝 Promise
  20.                     }
  21.                 }
  22.                 xhr.onerror = function(e) {
  23.                     onError && onError(xhr.statusText);
  24.                     reject({ status: xhr.status, data: xhr.statusText }); // 拒绝 Promise
  25.                 };
  26.                 xhr.send();
  27.             });
  28.         },
复制代码
这个示例进一步封装了 XMLHttpRequest,使其可以返回一个 Promise,方便进行异步处置惩罚。
3. 创建 a 标签下载

  1.         downloadFile(blob, fileName = '2.mp4') {
  2.             // 创建a 标签
  3.             const a = document.createElement('a');
  4.             const blobUrl = URL.createObjectURL(blob);
  5.             a.setAttribute('href', blobUrl);
  6.             a.setAttribute('download', fileName);
  7.             a.style.display = 'none';
  8.             document.body.appendChild(a);
  9.             a.click();
  10.             document.body.removeChild(a);
  11.             URL.revokeObjectURL(blobUrl)
  12.         },
复制代码
使用实例:
  1.     async downloadVideo(row) {
  2.             const url = row.path;
  3.             if (!url) {
  4.                 return;
  5.             }
  6.             var index = this.tableData.findIndex(item => item.id === row.id);
  7.             // 使用示例
  8.             const response = await this.xhrToDownload({ url: url }, (progress) => {
  9.                 console.log('Download progress:', progress);
  10.                 if (index !== -1) {
  11.                     this.tableData[index].downLoadProgress = progress
  12.                 }
  13.             }, (res) => {
  14.                 // 这里处理 Blob,例如保存到 IndexedDB、FileSystem API 或其他
  15.                 console.log('Download successful:', res);
  16.                 // 如果你确实需要下载文件,可以在这里创建 <a> 标签并触发点击
  17.             }, (error) => {
  18.                 console.error('Download failed:', error);
  19.             })
  20.             if (response && response.status === 200) {
  21.                 this.downloadFile(response.data)
  22.             }
  23.         },
复制代码
二、 Fetch API

1. 使用 Fetch API



  • 链式调用
    1.         fetch(url).then(res => {
    2.             return res.blob()
    3.         }).then(res => {
    4.             console.log('res', res);
    5.         })
    复制代码
  • async - await 语法糖
  1.         // const response = await fetch(url)
  2.         // const blod = await response.blob()
复制代码
2. 使用 Stream API 和 ReadableStream

Fetch API 支持相应体作为 ReadableStream,但直接使用它来跟踪进度可能不太直观。不过,你可以通过监听流的读取操作来大致估计进度(虽然这通常不如 XMLHttpRequest 那样准确)。
  1.     //your_file_url fetch('http://127.0.0.1:456/proxy/DJI_0003.MP4')
  2.     fetch('http://127.0.0.1:456/proxy/DJI_0003.MP4').then(response=>{
  3.         console.log(response);
  4.         const reader = response.body.getReader() //  ReadableStream
  5.         const contentLength = response.headers.get('content-length')
  6.         let readTotal = 0
  7.         if(!contentLength){
  8.             console.log('无法获取进度');
  9.             return
  10.         }
  11.         const sizeTotal = parseInt(contentLength)
  12.         const chunks =[]
  13.         function read(){
  14.             reader.read().then(({done,value})=>{
  15.                 if(done){
  16.                     console.log('下载完成');
  17.                     const type = response.headers.get('content-type')
  18.                     const blob = new Blob(chunks,{type})
  19.                     return
  20.                 }
  21.                 readTotal += value.length
  22.                 const progress = Math.ceil(readTotal/sizeTotal*100)
  23.                 console.log('下载进度:',progress);
  24.                 chunks.push(value)
  25.                 read()
  26.             })
  27.         }
  28.         read()
  29.     })
复制代码
注意:上面的代码示例并不直接计算下载进度,由于 ReadableStream API 并不直接提供总大小信息(除非你在相应头中通过 Content-Length 获取)。你需要有一个方式来获取文件的总大小,以便可以或许计算进度。
3. 使用fetch下载并获取进度

  1. 简单获取下载进度
  2.         fetchToDownlod(url, options, onProgress, onSuccess, onError) {
  3.             try {
  4.                 // eslint-disable-next-line no-async-promise-executor
  5.                 return new Promise(async(resolve, reject) => {
  6.                     const response = await fetch(url, options);
  7.                     const reader = response.body.getReader();
  8.                     // Step 2:获得总长度(length)
  9.                     const contentLength = +response.headers.get('Content-Length');
  10.                     console.log('contentLength', contentLength);
  11.                     // Step 3:读取数据
  12.                     let receivedLength = 0; // 当前接收到了这么多字节
  13.                     const chunks = []; // 接收到的二进制块的数组(包括 body)
  14.                     // eslint-disable-next-line no-constant-condition
  15.                     while (true) {
  16.                         const { done, value } = await reader.read();
  17.                         if (done) {
  18.                             // 如果没有更多的数据可读,则退出循环
  19.                             break;
  20.                         }
  21.                         chunks.push(value);
  22.                         receivedLength += value.length;
  23.                         const progress = Math.round(receivedLength / contentLength * 100);
  24.                         onProgress && onProgress(progress);
  25.                     }
  26.                     // 将响应体转换为 Blob 对象
  27.                     const blob = new Blob(chunks, { type: 'application/octet-stream' });
  28.                     if (response.status === 200) {
  29.                         resolve({ status: response.status, blob });
  30.                     }
  31.                     if (response.status === 404) {
  32.                         reject({ status: response.status, blob });
  33.                     }
  34.                 });
  35.             } catch (err) {
  36.                 console.log('err', err);
  37.                 return Promise.reject(err);
  38.             }
  39.         },
复制代码
调用实例:
  1.        async downloadVideo(row) {
  2.             const url = row.path;
  3.             if (!url) {
  4.                 return;
  5.             }
  6.             let fileName = 'text.mp4'
  7.             const lastIndex = url.lastIndexOf('/');
  8.             if (lastIndex !== -1) {
  9.                 fileName = url.substring(lastIndex + 1);
  10.             }
  11.             var index = this.tableData.findIndex(item => item.id === row.id);
  12.             const options = {
  13.                 method: 'GET', // *GET, POST, PUT, DELETE, etc.
  14.                 mode: 'cors', // no-cors, *cors, same-origin
  15.                 cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
  16.                 credentials: 'same-origin', // include, *same-origin, omit
  17.                 responseType: 'blob', //重要代码
  18.                 'Access-Control-Allow-Origin': '*',
  19.                 'Access-Control-Allow-Credentials': true,
  20.                 headers: {
  21.                     'Content-Type': 'application/json'
  22.                 }
  23.             }
  24.             const res = await this.fetchToDownlod(url, options, (progress) => {
  25.                 // console.log('Download progress:', progress);
  26.                 if (index !== -1) {
  27.                     this.tableData[index].downLoadProgress = progress
  28.                 }
  29.             })
  30.             console.log('res', res);
  31.             if (!res || res.status !== 200) {
  32.                 return this.$message.error('下载失败')
  33.             }
  34.             this.downloadFile(res.blob, fileName)
  35.         },
复制代码
结论

如果你的应用需要准确地跟踪下载进度,并且你的情况允许,使用 XMLHttpRequest 可能是最直接和简单的方法。如果你正在寻找更今世的方法,并可以接受一些复杂性,你可以考虑使用 Service Workers 或 Stream API 来实现类似的功能。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用多少眼泪才能让你相信

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表