来实现一个下载的需求需要展示实时下载进度 (进来不后悔)

4,574 阅读2分钟

在工作中我们可能有遇到这种情况,需要下载一个文件或者是视频的情况并且需要提供给用户实时的进度

首先想到的是使用fetch (浏览器提供的请求方法) fetch支持跟踪并且展示进度并且提供了一个response.body属性,

接下来一起看看具体实现过程

先创建一个index.html用来展示进度详情

   <div>
        进度:<span class="VALUE">0</span>%
        <p id="LINE"></p>
    </div>

加一些样式

  <style>
        *{
            padding: 0;
            margin: 0;
        }
        #LINE {
            height: 20px;
            width: 0;
            background: purple;
            color: #fff;
            text-align: right;
            padding-right: 20px;
            box-sizing: border-box;
        }
    </style>

我们需要启动一个服务用来编写接口和读取本地index.html(顺便解决跨域问题)

创建server.js ,创建完成之后启动node server

const http = require('http')
const fs = require('fs')

const server = http.createServer((req, res) => {

    if (req.url === '/') {
        res.writeHead(200, { 'Content-Type': 'text/html;charset=UTF-8' })

        const html = fs.readFileSync('./index.html', 'utf-8')

        res.end(html)
    } else if (req.url === '/api') {
        let data = ''
        //我们放一个视频mv1.mp4在我们目录下用于接口读取
        let readerStream = fs.createReadStream('./mv1.mp4');

        readerStream.on('data', function (chunk) {
            data += chunk;
        })

        readerStream.on('end', function () {
            res.writeHead(200, { 'Content-Type': 'video/mp4' })
            res.end(JSON.stringify(data))
        });

    }
})



server.listen(8080, () => console.log('启动中....'))

接下来继续编写index.html里面的js逻辑

 /**
   * 1.使用fetch返回的response.body属性
   * 2.response.body有一个getReader()方法
   * 3.我们需要循环调用getReader().read方法
   * 4.会返回一个对象包含done和value,如果done为true说明已经读取完成
   * 5.以上是我们具体逻辑
 */
//创建一个方法
async function getValue() {
  	
  //使用fetch读取我们写好的接口
	const response = await fetch('http://localhost:8080/api');
  const reader = response.body.getReader();
	
  //顺便提一下我们想要获取返回数据的大小可以通过Content-Length获取字节长度,NetWork中的size代表的是整个请求体的大小
  const contentLength = +response.headers.get('Content-Length');
  
  //设置初始值
  let count = 0
	
  VALUE.innerHTML = count
  
  let receiveLength = 0 //接收当前已经加载的字节
  
  let chunks = [] //接收到的二进制块的数组(包括body)
  
  //循环调用reader.read()方法
   while (true) {
     const {
       done,
       value
     } = await reader.read()

     if (done) {
       VALUE.innerHTML = 100
       break;
     }
     VALUE.innerHTML = VALUE.innerHTML < 99 ? ++count : VALUE.innerHTML
     LINE.style.width = VALUE.innerHTML < 99 ? ++count + '%' : VALUE.innerHTML + '%'
     chunks.push(value);
     receiveLength += value.length
   }
  
  //Uint8Array 数组类型表示一个8位无符号整型数组,创建时内容被初始化为0。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。
  
  let chunkAll = new Uint8Array(receiveLength)
  let position = 0
	
  //将值放到chunkAll中之后使用TextDecoder解析 (TextDecoder 接口表示一个文本解码器,一个解码器只支持一种特定文本编码,例如 utf-8、iso-8859-2、koi8、cp1261,gbk 等等。解码器将字节流作为输入,并提供代码点流作为输出)
  
  for (let chunk of chunks) {
    chunkAll.set(chunk, position)
    position += chunk.length
  }
	
  //最后的结果
  let res = new TextDecoder('utf-8').decode(chunkAll)
  
}

到这数据下载展示进度就算完了

在延伸一个面试题,有时候会有一些面试官问:怎么中断一个请求呢?

现在一般常用的请求方式有两种axios或者fetch

//1.axios提供了一个一个方法叫做cancelToken
const CancelToken = axios.CancelToken;
//2.CancelToken里面有个source()方法
const source = CancelToken.source();
//3.之后继续调用source里面提供的cancel方法取消请求并且可以传一些提示进去
source.cancel('提示')
//1.AbortController接口表示一个控制器对象,允许你根据需要中止一个或多个web请求
const controller = new AbortController();
//2.获取signal需要传到fetch中
const signal = controller.signal;
//3.进行fetch请求
fetch(url, {signal}).then(function(response) {})
//4.在想中断的时机调用abort就可以终止
controller.abort();

The end Thank You