前端培训-初级阶段-场景实战(2019-06-06)-下载文件&下载进度


思否
前端培训 实战 JavaScript

转载自 SegmentFault

前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。

这两天,碰到了不止一次前端下载的的问题。其实之前我写过一篇文章 download使用浅析,主要依靠 download 属性来实现浏览器端下载,因为是走浏览器的下载,所以没有进度条。今天我们就来说说我的解决方案。

  • sf 的一个问题,需要显示进度条。答案地址,问题地址

  • 一个朋友的问题,下载的文件需要 headers 验证,无奈只能 ajax 拿数据,但是拉回来的还是字符串,需要自己处理。

  • 一个朋友的问题,监测下载进度。

今天我们要讲什么?

  • 如何使用 download 属性,下载文件。 这节主要是讲如何使用,以及前端下载的核心操作。

  • 下载文件,并显示进度条。 这节是正常操作,如果你只为了解原理,看到这里就够了

  • 其他数据类型如何互相转换 这节就不一样了。因为之前的 api 是使用 blob 实现,但是 ajax 传回来的数据有好多种类型,我们如何将他们相互转换?

如何使用 download 属性,下载文件。

download使用浅析 这一文中已经介绍了,可以去看看。我这里简单说一下。<a>标签如果设置了 download 属性,他就会去下载这个地址。测试地址-原生 download 属性测试。

下载文件,并显示进度条

下载文件上面已经实现了,那我们先说说如何显示进度条。

显示进度条

其实浏览器也是有进度条的,但是咱们拿不到。那我们就来模拟一下载,然后显示进度条。

ajax 实现下载进度条,测试地址-显示进度条

xhr = new XMLHttpRequest();
xhr.open('get', file1.url);
xhr.onprogress = (e)=>console.log(e)//e 就是一个 ProgressEvent 对象,其中 loaded 是已下载的, total 是总大小。
xhr.send()

fetch 实现下载进度条,测试地址-fetch显示进度条并下载 fetch 的实现上来说有一些功能是没有的,比如 abort、进度等。那我们就需要去通过一些别的手段来模拟实现。 实现代码如下,我们操作成读流,然后统计长度。

下载文件

进度条已经显示好了,那我们可以下载文件了。首先我们要分几种情况

  • 缓存下载(一个资源如果已经下载完了,再次去访问)

本地下载(资源已经在浏览器中)

blob url 下载 如这种地址 blob:https://www.lilnong.top/deb4c297-821c-4545-9b23-0fbdd76890c7

base64 url 下载 如这种地址 data:application/octet-stream;base64,aGVsbG8gbGlub25n

  blob = new Blob(['hello linong'])
  freader = new FileReader()
  freader.readAsDataURL(blob)//将 blob 读成 dataurl
  freader.onload=e=>console.log(freader.result)// 异步的,所以需要回调里面拿
  • 无缓存下载(资源没在本地,也没有缓存)

情况就是上面几种,那我们要做的其实就是统一一下流程

  • ajax 拉取数据(显示进度条)

缓存了数据,然后下载缓存(因为是缓存,所以秒下)

浏览器缓存 我比较推荐用这个,因为其他的方案都有大小或者兼容上的问题。但是这个缓存需要服务器设置需要走缓存。

bloburl 本地缓存下载 这个方案在移动端异常,pc端正常,感兴趣的小伙伴可以自己实现一下,毕竟学了这么多,得用起来才能变成自己的

dataurl 本地缓存下载 这个方案的支持会比 bloburl 好一点,但是只适用于小文件。

实现前端下载文件并显示进度条

我们让 ajax 直接返回 blob。然后构建 bloburl 用于下载。

downloadFile2 = (url)=>{
    var xhr = new XMLHttpRequest();
    xhr.open('get', url);
    xhr.responseType='blob';//这是精髓
    xhr.onprogress = onprogress2;//下载进度
    // .upload.onprogress 这个是上传的时候的进度
    xhr.onreadystatechange = ()=>{
        if(xhr.readyState == 4 && xhr.status == 200){
            nativeDownload(URL.createObjectURL(xhr.response))
        }
    }
    xhr.send()
}

其他类型转换为 blob

如果是一些封装过的 ajax,没办法使用 xhr.responseType='blob' 之类的,返回回来是字符串。那我们需要怎么出转换呢?

blob to *

blob 需要配合 FileReader 来读取

blob to arrayBufferreadAsArrayBuffer) 通用的、固定长度的原始二进制数据缓冲区

var fileReader = new FileReader();
fileReader.readAsArrayBuffer(xhr.response);//xhr.reponse 是 blob 类型
fileReader.onload = e=>console.log(fileReader.result);

blob to DataURLreadAsDataURL

Base64 是一组相似的二进制到文本(binary-to-text)的编码规则,使得二进制数据在解释成 radix-64 的表现形式后能够用 ASCII 字符串的格式表示出来。Base64 这个词出自一种 MIME 数据传输编码。 —MDN

blob to TextreadAsText) 以字符串表示所读取的文件内容

blob to BinaryStringreadAsBinaryString) 文件的原始二进制数据

* to blob

arrayBuffer to blob new Blob([arrayBuffer], {type: 'image/jpeg'})

base64 to blob

(new Uint8Array(Array.from(atob(base64url.split(',')[1])).map(v=>v.charCodeAt()))).buffer
//base64url.split(',')[1] //截取不要 data:images/jpeg;base64, 这串
//atob //转换成 BinaryString
//Array.from //转换成数组
//map(v=>v.charCodeAt()) //转换成对应的 ascii 码
//Uint8Array 转换成 Uint8Array 然后输出 buffer

BinaryString to blob 方案同上,因为上面的也是转换成了BinaryString

Text to blob ajax 默认就是 Text 类型的返回值。这个我觉得是编码类型的转换,比如 utf-8 to ascii,目前我还没找到好的实现方法。

总结

之前就写过一篇AJAX 的进阶使用(Blob、ArrayBuffer、FormData、Document、JSON、Text),里面讲了这些支持的类型。 base64转换上传,也写过这样的。

前端目前需要操作的东西越来越多了。

在最上面那个问答里,有个库去实现 download 操作。实现原理可以自己去看看。

后记

参考资料

  • Data URLs —MDN

  • StringView

  • 字符编码中ASCII、Unicode和UTF-8的区别

  • String​.prototype​.char​CodeAt()

  • DOMString

  • rfc2397


原文:SegmentFault

评论

评论功能即将开放。你也可以通过 邮件GitHub Issue 反馈。