灏天阁

文件操作

· Yin灏

文件操作简介

在 HTML5 中,文件上传是使用 input 元素来实现的,其中 type 属性取值为 “file”。

<input type="file" />

文件上传 input 元素有两个重要属性:multiple 和 accept。multiple 属性表示“是否选择多个文件”。其中,下面两句代码是等价的:

<input type="file" multiple />
<input type="file" multiple="multiple" />

accept 属性用于设置文件的过滤类型(MIME类型),常见的 accept 属性取值如表:

如果想要同时设置多个过滤类型,可以使用英文逗号(,)隔开,例如:

<input type="file" accept="image/jpeg, image/png"/>

举例:选择单个文件

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title></title>
</head>
<body>
  <form method="post">
    <input type="file" />
  </form>
</body>
</html>

举例:选择多个文件

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title></title>
</head>
<body>
  <form method="post">
    <input type="file" multiple/>
  </form>
</body>
</html>

为元素添加 multiple 属性后,就可以选择多个文件了。当选择成功后,按钮右侧不再显示文件的名称,而是显示文件的总量。当将鼠标指针移到上面时,就会显示全部上传文件的详细列表,

举例:accept 属性

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title></title>
</head>
<body>
  <form method="post">
    <input type="file" accept="image/png" multiple/>
  </form>
</body>
</html>

accept="image/png" 表示只可以上传后缀名为 .png 格式的文件(也就是 PNG 图片),如果选择其他文件,则是无法上传的。

默认的 <input type="file" /> 元素比较难看,在实际开发中,为了获得更好的用户体验,我们一般都是使用 “opacity: 0;” 来隐藏原来的表单元素,然后在上面覆盖一个美化的按钮或者提示语。

举例:file 类型元素的样式美化

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <style>
      /*去除默认样式*/
      * {
        margin: 0;
        padding: 0;
      }
      .container {
        width: 160px;
        margin: 30px auto;
      }
      .filePicker {
        position: relative;
        width: 160px;
        height: 44px;
        line-height: 44px;
        text-align: center;
        color: #ffffff;
        background: #00b7ee;
      }
      .filePicker input[type="file"] {
        position: absolute;
        top: 0;
        left: 0;
        width: 160px;
        height: 44px;
        opacity: 0;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <!--默认时的表单-->
    <div class="container">
      <input type="file" />
    </div>
    <!--美化后的表单-->
    <div class="container">
      <div class="filePicker">
        <label>点击选择文件</label>
        <input id="fileInput" type="file" accept="image/*" multiple />
      </div>
    </div>
  </body>
</html>

分析:

想要对 file 类型元素进行美化,我们都是这样处理的:首先使用 opacity: 0; 将表单设置为透明,然后使用绝对定位在表单原来的位置上面定义一个 label 就可以了。

这里还要说明一下,使用了 opacity:0; 之后,虽然表单看不见了,但它并没有消失,还占据着原来的位置,因此,我们点击原来的位置,还可以继续使用表单的功能。这个地方非常巧妙,也是实现的关键。

File 对象

在文件上传元素中,将会产生一个 FileList 对象,它是一个类数组对象,表示所有文件的集合。其中,每一个文件就是一个 File 对象。

想要获取某一个文件对象(即 File 对象),我们首先需要获取 FileList 对象,然后再通过数组下标形式来获取。

File 对象的属性

举例:File 对象的属性

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        //获取FileList对象
        var oFile = document.getElementById("file");
        oFile.onchange = function () {
          //获取第1个文件,即File对象
          var file = oFile.files[0];
          console.log("图片名称为:" + file.name);
          console.log("图片大小为:" + file.size + "B");
          console.log("图片类型为:" + file.type);
          console.log("修改时间为:" + file.lastModifiedDate);
        };
      };
    </script>
  </head>
  <body>
    <input id="file" type="file" accept="image/*" multiple />
  </body>
</html>

当我们点击【选择文件】按钮,选择一张本地图片后,此时在浏览器控制台中会输出图片的名称、大小、类型以及修改时间,

由于 file.size 获取到的大小值的单位是 B(字节),在实际开发中,我们大多数情况下都会将 file.size 换算为常见的文件大小单位,请看下面的例子:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oFile = document.getElementById("file");
        oFile.onchange = function () {
          //获取第1个文件
          var file = oFile.files[0];
          //将单位“B”转化为“KB”
          var size = file.size / 1024;
          var unitArr = ["KB", "MB", "GB", "TB"];
          //转化单位
          for (var i = 0; size > 1; i++) {
            var fileSizeString = size.toFixed(2) + unitArr[i];
            size /= 1024;
          }
          //输出结果
          console.log("图片大小为:" + fileSizeString);
        };
      };
    </script>
  </head>
  <body>
    <input id="file" type="file" accept="image/*" />
  </body>
</html>

FileReader 对象

在 HTML5 中,专门提供了一个文件操作的 API,即 FileReader 对象。我们通过 FileReader 对象可以很方便地读取文件中的数据。

FileReader 对象有 5 个方法,其中 4 个用来读取文件数据,另外 1 个用于中断读取操作

FileReader 对象的方法

readAsText() 方法有两个参数:第 1 个参数为 File 对象,第 2 个参数为文本的编码方式,默认值为 “utf-8”。这个方法非常容易理解,表示将文件以文本方式读取,读取的结果就是这个文本文件的内容。

readAsBinaryString() 方法将文件读取为二进制字符串,通常我们将它传送到后端,后端可以通过这段字符串来存储文件。

abort() 方法可以用来中止读取操作,在读取大文件时,这个方法非常有用。

FileReader 对象提供了6个事件,用于检测文件的读取状态,如表:

FileReader 对象的事件

var reader = new FileReader();
reader.readAsText(file, 编码);
reader.onload = function(){
  //成功读取后的操作
};

举例:读取TXT文本(readAsText()方法)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oFile = document.getElementById("file");
        oFile.onchange = function () {
          //获取第1个文件
          var file = oFile.files[0];
          //读取本地文件,以GBK编码方式输出
          var reader = new FileReader();
          reader.readAsText(file, "gbk");
          reader.onload = function () {
            console.log(this.result);
          };
        };
      };
    </script>
  </head>
  <body>
    <input id="file" type="file" />
  </body>
</html>
reader.onload = function(){
  this.result;
};

对于上面这段代码,一旦开始读取文件,无论成功或失败,实例的 result 属性(即 this.result)都会被填充。如果读取成功,则 this.result 的值就是当前文件的内容;如果读取失败,则 this.result 的值为 null。

举例:在线预览图片(readAsDataURL()方法)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oFile = document.getElementById("file");
        oFile.onchange = function () {
          //获取第1个文件
          var file = oFile.files[0];
          //将图片转换为Base64格式
          var reader = new FileReader();
          reader.readAsDataURL(file);
          reader.onload = function () {
            //添加图片到页面中
            var oImg = document.createElement("img");
            oImg.src = this.result;
            document.body.appendChild(oImg);
          };
        };
      };
    </script>
  </head>
  <body>
    <input id="file" type="file" /><br />
  </body>
</html>

我们都知道,“img 元素的 src 属性”或者“其他元素的 background 属性的 url”,都可以被赋值为 Base64 编码,然后显示图片。在 HTML5 以前,我们一般都是先将本地图片上传到服务器,等上传成功后再由后台返回图片的地址在前端显示。到了 HTML5 时代,我们使用 FileReader 对象的 readAsDataURL() 方法,可以不经过后台而直接将本地图片显示在页面中,这样可以减少前后端的频繁交互,减少服务器端的压力。

举例:拖曳文件并读取

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <style type="text/css">
      #box {
        width: 150px;
        height: 150px;
        border: 1px solid silver;
      }
    </style>
    <script>
      window.onload = function () {
        var oBox = document.getElementById("box");
        var oContent = document.getElementById("content");
        //阻止默认行为
        oBox.ondragover = function (e) {
          e.preventDefault();
        };
        //添加ondrop事件
        oBox.ondrop = function (e) {
          e.preventDefault();
          //读取文本
          var file = e.dataTransfer.files[0];
          var reader = new FileReader();
          reader.readAsText(file, "gbk");
          reader.onload = function () {
            //把文本内容添加到页面
            oContent.innerHTML = this.result;
          };
        };
      };
    </script>
  </head>
  <body>
    <div id="box"></div>
    <p id="content"></p>
  </body>
</html>

当我们拖动本地的一个 TXT 文件到页面的框中时,就会读取 TXT 文件中的内容,并且添加到页面中,

举例:拖曳图片并预览

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <style type="text/css">
      #box {
        width: 150px;
        height: 150px;
        border: 1px solid silver;
      }
    </style>
    <script>
      window.onload = function () {
        var oBox = document.getElementById("box");
        var oContent = document.getElementById("content");
        //阻止默认行为
        oBox.ondragover = function (e) {
          e.preventDefault();
        };
        //添加ondrop事件
        oBox.ondrop = function (e) {
          e.preventDefault();
          //获取File对象
          var file = e.dataTransfer.files[0];
          var reader = new FileReader();
          reader.readAsDataURL(file);
          reader.onload = function () {
            var oImg = document.createElement("img");
            //设置图片src为this.result
            oImg.src = this.result;
            oImg.style.width = "150px";
            oImg.style.height = "150px";
            oBox.appendChild(oImg);
          };
        };
      };
    </script>
  </head>
  <body>
    <div id="box"></div>
  </body>
</html>

当我们拖动本地的一个图片到页面的框中时,就会把图片添加到页面中。

Blob 对象

在 HTML5 中,还新增了一个 Blob 对象,用于代表原始二进制数据。实际上,前面介绍的 File 对象也继承于 Blob 对象。

var blob = new Blob(dataArray, type);

Blob() 构造函数有两个参数,这两个参数都是可选参数,而并非必选参数。

第1个参数 dataArray 是一个数组,数组中的元素可以是以下类型的对象:

  • String对象(即字符串)
  • Blob对象(即其他Blob对象)
  • ArrayBuffer对象
  • ArrayBufferView对象

第 2 个参数 type 是一个字符串,表示 Blob 对象的 MIME 类型。

举例:创建并下载TXT文件

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oTxt = document.getElementById("txt");
        var oBtn = document.getElementById("btn");
        var oDiv = document.getElementById("container");
        oBtn.onclick = function () {
          var text = oTxt.value;
          var blob = new Blob([text], { type: "text/plain" });
          //通过createObjectURL()方法创建文字链接
          oDiv.innerHTML =
            '<a download href="' +
            window.URL.createObjectURL(blob) +
            '" target="_blank">下载文件</a>';
        };
      };
    </script>
  </head>
  <body>
    <textarea id="txt" cols="30" rows="10"></textarea><br />
    <input id="btn" type="button" value="创建链接" />
    <div id="container"></div>
  </body>
</html>

Blob 对象可以通过 window.URL 对象的 createObjectURL() 方法生成一个网络地址,然后结合 a 标签的 download 属性来实现下载文件的功能。

如果想要创建及下载 HTML 文件,只需要把 var blob = new Blob([text],{type:"text/plain"}); 改为 var blob = new Blob([text],{type: "text/html"}); 就可以了。

此外,想要重命名文件,可以使用 a 元素的 download 属性。

上面这个例子需要额外创建一个文本链接,然后点击该文本链接才会实现下载文件的功能。如果我们不希望创建多余的元素,而是直接点击按钮就能实现下载,此时又该怎么实现呢?

举例:改进版(无须添加多余元素)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script>
      window.onload = function () {
        var oTxt = document.getElementById("txt");
        var oBtn = document.getElementById("btn");
        var oDiv = document.getElementById("container");
        oBtn.onclick = function () {
          //Blob中数据为“文字”,默认编码为“utf-8”
          var text = oTxt.value;
          var blob = new Blob([text], { type: "text/plain" });
          //通过createObjectURL()方法创建文字链接
          var oA = document.createElement("a");
          var url = window.URL.createObjectURL(blob);
          oA.download = "download";
          oA.href = url;
          //添加元素
          document.body.appendChild(oA);
          //触发点击
          oA.click();
          //移除元素
          document.body.removeChild(oA);
        };
      };
    </script>
  </head>
  <body>
    <textarea id="txt" cols="30" rows="10"></textarea><br />
    <input id="btn" type="button" value="下载文件" />
  </body>
</html>

实际上,除了 TXT 文件,像 HTML 文件、JSON 文件等,也可以利用这种小技巧来实现文件下载操作。

举例:将 Canvas 下载为一个图片文件

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <script type="text/javascript">
      function $$(id) {
        return document.getElementById(id);
      }
      window.onload = function () {
        var cnv = $$("canvas");
        var cxt = cnv.getContext("2d");
        //定义文字
        var text = "绿叶学习网";
        cxt.font = "bold 60px 微软雅黑";
        //定义阴影
        cxt.shadowOffsetX = 5;
        cxt.shadowOffsetY = 5;
        cxt.shadowColor = "#66CCFF";
        cxt.shadowBlur = 10;
        //填充文字
        cxt.fillStyle = "#FF6699";
        cxt.fillText(text, 10, 90);
        //点击按钮,下载图片
        $$("btn").onclick = function () {
          cnv.toBlob(
            function (blob) {
              //通过createObjectURL()方法创建文字链接
              var oA = document.createElement("a");
              var url = window.URL.createObjectURL(blob);
              //此处设置图片名称
              oA.download = "download";
              oA.href = url;
              //添加元素
              document.body.appendChild(oA);
              //触发点击
              oA.click();
              //移除元素
              document.body.removeChild(oA);
            },
            "image/jpeg",
            1
          );
        };
      };
    </script>
  </head>
  <body>
    <canvas
      id="canvas"
      width="320"
      height="150"
      style="border: 1px solid silver"
    ></canvas
    ><br />
    <input id="btn" type="button" value="下载图片" />
  </body>
</html>

我们可以使用 Canvas 对象的 toBlob() 方法来创建一个 Blob 对象。其中,toBlob() 方法有3个参数:

  • 第1个参数是一个回调函数;
  • 第2个参数是图片类型(默认值为image/png);
  • 第3个参数是图片质量(取值为0~1),

语法格式如下:

cnv.toBlob(function(blob){
  //...
}, "image/jpeg", 0.5);

在实际开发中,FileReader 对象的 readAsText()、readAsArrayBuffer() 等方法可以读取文件数据,然后结合 Blob 对象下载文件的功能,就可以实现将数据导出文件备份到本地。当要恢复数据时,通过 input 元素上传备份文件,使用 readAsText()、readAsArrayBuffer() 等方法读取文件,即可恢复数据。

- Book Lists -