• HTML5 File API


    HTML5 引入了一个 File API 用以提供用户上传文件的信息,并允许网页中的 JavaScript 访问其内容。

    以下是一些表单 file 控件:

    <input type="file" accept="video/*;capture=camcorder">
    <input type="file" accept="audio/*;capture=microphone">
    <input type="file" accept="image/*;capture=camera">直接调用相机(测试安卓可以,iphone还是有相册)
    <input type="file" accept="image/*" />调用相机 图片或者相册
    <input type="file" multiple accept="image/*" />调用相册

    1 FileList 对象

    FileList 对象针对表单的 file 控件。 当用户通过 file 控件选取文件后,这个控件的 files 属性值就是 FileList 对象。

    // 多选控件

    <input type='file' multiple />
    <script>
    document.querySelector('input').onchange = function() {
    console.log(this.files);
    
    };
    </script>


    控制台输出:

    FileList {0: File, 1: File, length: 2}
    0: File
    1: File
    length:2
    __proto__: Object


    除了用 file 控件,采用拖放方式,也可以得到 FileList 对象。

    <textarea></textarea>
    <script>
      var ipt = document.querySelector('textarea');
      ipt.ondragover = function () { return false; };
    
      // Add drop handler
      ipt.ondrop = function(e) {
        e.stopPropagation();
        e.preventDefault();
        e = e || window.event;
        var files = e.dataTransfer.files;
    
        console.log(files);
      };
    </script>

    选中两个文件拖放到文本框中,打印结果和上面一致。
    一般来说,我们不可能手动构造 FileList 对象,只能被动地读取,也就是说只有用户主动触发了文件读取行为,js 才能访问到 FileList,而这通常发生在表单选择文件或者拖拽文件中。

    2 File 对象

    我们看到一个 FileList 对象包含了我们选中的 File 对象,那么一个 File 又有哪些属性呢?我们可以打印出来看看。

    • name:文件名,该属性只读。
    • size:文件大小,单位为字节,该属性只读。
    • type:文件的 MIME 类型,如果分辨不出类型,则为空字符串,该属性只读。
    • lastModified:文件的上次修改时间,格式为时间戳。
    • lastModifiedDate:文件的上次修改时间,格式为 Date 对象实例。

    3 Blob 对象

    上图中我们看到,File 对象是继承自 Blob 对象的,Blob 又是什么鬼?

    Blob(Binary Large Object)对象代表了一段二进制数据,提供了一系列操作接口。其他操作二进制数据的 API(比如 File 对象),都是建立在 Blob 对象基础上的,继承了它的属性和方法。

    生成 Blob 对象有两种方法:一种是使用 Blob 构造函数,另一种是对现有的 Blob 对象使用 slice 方法切出一部分。

    (1)Blob 构造函数,接受两个参数。第一个参数是一个包含实际数据的数组,第二个参数是数据的类型,这两个参数都不是必需的。

    var a = ["hello", "world"];
    var myBlob = new Blob(a, { "type" : "text/xml" });
    console.log(myBlob);
    
    

    (2)Blob 对象的 slice 方法,将二进制数据按照字节分块,返回一个新的 Blob 对象。

    var a = ["hello", "world"];
    var myBlob = new Blob(a, { "type" : "text/xml" });
    var newBlob = myBlob.slice(0, 5);
    console.log(newBlob);
    
    

    Blob 对象有两个只读属性:

    • size:二进制数据的大小,单位为字节。(文件上传时可以在前端判断文件大小是否合适)
    • type:二进制数据的 MIME 类型,全部为小写,如果类型未知,则该值为空字符串。(文件上传时可以在前端判断文件类型是否合适)

    4FileReader

    重头戏来了,FileReader API 才是我们接下去完成一些任务的关键。

    FileReader API 用于读取文件,即把文件内容读入内存。它的参数是 File 对象或 Blob 对象

    对于不同类型的文件,FileReader 提供不同的方法读取文件。

    • readAsBinaryString(Blob|File):返回二进制字符串,该字符串每个字节包含一个 0 到 255 之间的整数。(已废弃)
    • readAsText(Blob|File, opt_encoding):返回文本字符串。默认情况下,文本编码格式是 UTF-8,可以通过可选的格式参数,指定其他编码格式的文本。
    • readAsDataURL(Blob|File):返回一个基于 Base64 编码的 data-uri 对象。
    • readAsArrayBuffer(Blob|File):返回一个 ArrayBuffer 对象。

    除了以上四种不同的读取文件方法,FileReader API 还有一个 abort 方法,用于中止文件上传。

    var reader = new FileReader();
    reader.abort();

    FileReader 对象采用异步方式读取文件,可以为一系列事件指定回调函数。

    • onabort 方法:读取中断或调用 reader.abort() 方法时触发。
    • onerror 方法:读取出错时触发。
    • onload 方法:读取成功后触发。
    • onloadend 方法:读取完成后触发,不管是否成功。触发顺序排在 onload 或 onerror 后面。
    • onloadstart 方法:读取将要开始时触发。
    • onprogress 方法:读取过程中周期性触发。(可以用来获取文件读取的进度)

    以前在学习图片的 base64 编码的时候,写了一篇文章 获取图片 base64 编码的几种方法

    当时还没有学习 File API,了解到 File API 也能做类似的事情,现在学到了,

    写了个简单的 demo http://hanzichi.github.io/2016/image2base64/,不仅能获取图片的 base64 编码,

    同时也能获取文字的 base64 编码。

    <title>获取图片 base64 编码</title>
    <meta charset="utf-8">
    <style>
      textarea {
        width: 100%;
        height: 200px;
        margin-top: 10px;
      }
    </style>
    <body>
    <h1>上传图片或者拖动图片到框内获取图片 base64 编码</h1>
    <input type='file' multiple /><br/>
    <textarea></textarea>
    <script>
      document.querySelector('input').onchange = function() {
        var reader = new FileReader();
        reader.onload = function() {
          var dataURL = reader.result;
          document.querySelector('textarea').innerHTML = dataURL;
        };
        reader.readAsDataURL(this.files[0]);
      };
      // drag & drop
      var ipt = document.querySelector('textarea');
      ipt.ondragover = function () { return false; };
      // Add drop handler
      ipt.ondrop = function(e) {
        e.stopPropagation();
        e.preventDefault();
        e = e || window.event;
        var files = e.dataTransfer.files;
        var reader = new FileReader();
        reader.onload = function() {
          var dataURL = reader.result;
          document.querySelector('textarea').innerHTML = dataURL;
        };
        reader.readAsDataURL(files[0]);
      };
    </script>
    </body>

    获取到了文件的 base64 编码,做一些诸如图片预览的功能,也就手到擒来了,有兴趣的可以自己尝试下,

    类似的还有文字预览啊,等等。

    5 URL

    你以为 File API 就这样了吗?非也,还有个强大的东西没有介绍,URL 对象!

    调用 URL 对象的 createObjectURL 方法,传入一个 File 对象或者 Blob 对象,能生成一个链接,听起来好像很吊的样子。

    var objecturl =  window.URL.createObjectURL(blob);

    上面的代码会对二进制数据生成一个 URL,这个 URL 可以放置于任何通常可以放置 URL 的地方,比如 img 标签的 src 属性。需要注意的是,即使是同样的二进制数据,每调用一次 URL.createObjectURL 方法,就会得到一个不一样的 URL。

    这个 URL 的存在时间,等同于网页的存在时间,一旦网页刷新或卸载,这个 URL 就失效。(File 和 Blob 又何尝不是这样呢)除此之外,也可以手动调用 URL.revokeObjectURL 方法,使 URL 失效。

    window.URL.revokeObjectURL(objectURL);

    举个简单的例子。

    var blob = new Blob(["Hello hanzichi"]);
    var a = document.createElement("a");
    a.href = window.URL.createObjectURL(blob);
    a.download = "a.txt";
    a.textContent = "Download";
    
    document.body.appendChild(a);

    页面上生成了一个超链接,点击它就能下载一个名为 a.txt 的文件,里面的内容是 Hello hanzichi

    这里插点题外话,简单介绍下 H5 新增的 download 属性。

    是否支持download属性的监测
    要监测当前浏览器是否支持download属性,一行JS代码就可以了,如下:

    var isSupportDownload = 'download' in document.createElement('a');

    对于一些诸如 exe,rar 等浏览器不能直接打开的文件类型,我们一般可以直接用一个 a 标签,将其指向文件在服务端的地址,点击即可下载。但是如果是一些浏览器能直接打开的文件,比如 txt,js 等,如果这样设置一个超链接,点击会直接打开文件,一般我们可以配合后端实现,比如用 PHP。

    $file_name = "1.txt"; // 下载文件名
    $file_dir = dirname(__FILE__). '/'; //下载文件存放目录
    //输入文件标签
    Header("Content-type: text/plain");
    Header("Content-Disposition: attachment; filename=" . $file_name );

    以上代码需要文件的 Content-type 属性值,安利一个网址,http://tool.oschina.net/commons ,各种文件类型的 Content-type 属性值一网打尽!

    如果考虑到安全性,header + fread 可能会显得更严谨。

    $file_name = "1.txt"; // 下载文件名
    $file_dir = dirname(__FILE__). '/'; //下载文件存放目录
    
    Header("Content-type: text/plain");
    Header("Content-Disposition: attachment; filename=" . $file_name );
    echo fread($file, filesize($file_dir . $file_name));

    但是现在我们只需要在 a 标签上加上 download !

    <a href="1.txt" download>download txt></a>


    还可以给 download 加上属性值,即为下载的文件名。

    <a href="1.txt" download="2.txt">download txt></a>

    可以省略 .txt 的后缀名,浏览器会自行判断。

    我们再回到 URL 上来。对于 File 或者 Blob 对象,我们可以这样理解,它们的存在,依赖于页面,而 URL 能给这些 "转瞬即逝" 的二进制对象一个临时的指向地址。

    这个临时的地址还有什么用呢?也能做图片预览,相比前面用 readAsDataURL 的实现,更简单了。

    <input type='file' multiple /><br/>
    <img />
    <script>
    document.querySelector("input").onchange = function() {
      var files = this.files;
      document.querySelector("img").src = window.URL.createObjectURL(files[0]);
    }
    </script>
    
    

    比如还有这样的需求,前端上传文件,要动态生成该文件的下载链接,也能用 URL 完成。

    6 onprogress 事件

    想要上传图片的时候显示进度条,就要使用上 HTML5 中的 onprogress 事件了。
    onprogress 事件可以作用于 <video> 或 <audio> 标签中,作为其状态回调函数的作用。
    也可以是作为 XMLHttpRequest 的指定事件,XMLHttpRequest 对象,传送数据的时候,有一个 progress 事件,用来返回进度信息。
    它分成 上传和下载 两种情况:

    下载的 progress 事件属于 XMLHttpRequest 对象
    上传的 progress 事件属于 XMLHttpRequest.upload 对象
    当上传或下载时,会频繁调用该方法。该方法接受一个事件参数 event,其中 event.total 是需要传输的总字节,event.loaded 是已经传输的字节。如果event.lengthComputable 不为真,则 event.total 等于 0。

    例子代码:

    function uploadFile1(file,desc,catid){
    var $process = $("#process_"+file.size);
    var formData = new FormData(); //以下为像后台提交图片数据
    formData .append('img_url', file);
    formData .append('img_desc',desc);
    formData .append('cat_id',catid);
    var xhr=new XMLHttpRequest();
    xhr.open('post','shop_gallery.php?act=update',true);
    xhr.onreadystatechange=function (){
    if(this.readyState==4){
    $process.css("width","0%");
    if (!(img_url.length == 0)) {
    img_desc.splice(0, 1);
    uploadFile1(img_url[0],img_desc[0],catid);
    }
    }
    }
    xhr.upload.onprogress=function (ev){
    if(ev.lengthComputable){
    var precent=100 * ev.loaded/ev.total;
    $process.css("width",precent+'%');
    if (precent == 100){
    delimg1($process);
    if (img_url.length == 0){
    setTimeout("refresh("+catid+")", 1000 );
    }
    }
    }
    }
    // 
    xhr.send(formData);
    }

    7 Canvas & dataURL & Blob

    canvas 中有 toDataURL 函数,可以将 canvas 转为 dataURL 形式的 base64 编码,而 Blob 也可以转为 dataURL,这三者之间是否可以互相转换?有没有什么实用之处?

    (1)canvas -> dataURL

    用 toDataURL 方法,比较简单,不多说。

    (2)blob -> dataURL

    用 FileReader 的 readAsDataURL 方法,使用方式可以看

    (3)dataURL -> blob

    这个函数有点屌

    function dataURLtoBlob(dataurl) {
      var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
      while(n--){
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr], {type:mime});
    }
    
    

    (4)dataURL - canvas

    将 image 的 src 属性置为 dataURL,再用 drawImage 方法画上去。

    (5)blob - canvas

    如何把二进制形式的图片画上 canvas?先用 readAsDataURL 转为 dataURL,接着就是 (4) 的事情了。

    (6)canvas - blob

    canvas 转为 blob 也可以用 dataURL 做跳板,先将 canvas 转为 dataURL(1),再用 dataURL 转为 blob(3)。

    利用它们之间的转换可以做些什么好玩的事呢?比如可以上传图片,对图片做各种处理,然后保存。

    canvas 有原生的 toBlob 方法,使得图片文件可以被缓存或保存到本地。

    Read more

    File API 还能实现很多炫酷的功能,比如查看文件读取的进度(onprogress ),分割文件,分段读取文件,还能配合 ajax 使用,有兴趣的可以参考以下文献自行研究。

     
     
  • 相关阅读:
    link和@import引入css 区别,不建议使用@import
    centos8常用配置记录
    python获取txt文件中关键字的数量
    U盘装Centos7的异常记录
    Linux下密码安全策略配置
    CPU负载及利用率解析
    curl命令的简单使用(ftp)
    JVM工作原理及参数说明
    grafana使用mysql做数据源
    ZABBIX监控ESXI主机
  • 原文地址:https://www.cnblogs.com/cangqinglang/p/10114820.html
Copyright © 2020-2023  润新知