乐趣区

关于前端上传文件全面基础扫盲贴四-FileReader

FileReader 对象(知识点主要来源于关于 FileReader API)

摘自上面来源, 分析的挺好, 我又无耻得搬下来了:

使用 FileReader 对象,web 应用程序可以异步的读取存储在用户计算机上的文件 (或者原始数据缓冲) 内容, 可以使用 File 对象或者 Blob 对象来指定所要处理的文件或数据. 其中 File 对象可以是来自用户在一个 <input> 元素上选择文件后返回的 FileList 对象, 也可以来自拖放操作生成的 DataTransfer对象, 还可以是来自在一个 HTMLCanvasElement 上执行 mozGetAsFile()方法后的返回结果.

在这里作用就是当你上传图片之后, 可以直接从本地先读取出原始数据, 然后在页面上展示出来, 就是传说中的预览图片功能, 在上传到后台前就已经能先拿原始数据来使用了

看看兼容性如何(温馨提示: 下图只代表支持程度, 支持归支持, 不一定百分百支持, 所以用到部分方法时不兼容时正常的)

创建一个 FileReader 对象:

var reader = new FileReader();

事件处理程序:

事件 描述
onabort 当读取操作被中止时调用
onerror 当读取操作发生错误时调用
onload 当读取操作成功完成时调用
onloadend 当读取操作完成时调用, 不管是成功还是失败. 该处理程序在 onload 或者 onerror 之后调用
onloadstart 当读取操作将要开始之前调用
onprogress 在读取数据过程中周期性调用
abort 中止该读取操作. 在返回时,readyState 属性的值为 DONE. 当该 FileReader 对象没有在进行读取操作时 (也就是 readyState 属性的值不为 LOADING 时), 调用 abort() 方法会抛出异常 DOM_FILE_ABORT_ERR

下面方法会开始读取指定的 Blob 对象或 File 对象中的内容. 当读取操作完成时,readyState 属性 的值会成为 DONE, 如果设置了onloadend 事件处理程序, 则调用之. 区别在于:

属性 描述
readAsArrayBuffer result 属性中将包含一个 ArrayBuffer 对象以表示所读取文件的内容
readAsBinaryString result 属性中将包含所读取文件的原始二进制数据
readAsDataURL result 属性中将包含一个 data: URL 格式的字符串以表示所读取文件的内容.(这个就是实现我们预览的重要方法了!!!!!)
readAsText result 属性中将包含一个字符串以表示所读取的文件内容

介绍到这里就差不多了, 接下来看看怎么实现不提交后台实现预览图片, 这里只展示这部分功能先

示例

<!doctype html>
<html>

  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <title></title>
    <script type="text/javascript">
      var oFReader = new FileReader();

      oFReader.onload = function (oFREvent) {
        document
          .getElementById("uploadPreview")
          .src = oFREvent.target.result;
      };

      function loadImageFile() {if (document.getElementById("uploadImage").files.length === 0) 
          return;
        var oFile = document
          .getElementById("uploadImage")
          .files[0];
        oFReader.readAsDataURL(oFile);
      }
    </script>
  </head>

  <body>
    <form name="uploadForm">
      <img id="uploadPreview"/>
      <input id="uploadImage" type="file" name="myPhoto" onchange="loadImageFile();"/>
    </form>
  </body>

</html>

你看, 其实很简单, 没什么复杂代码, 只是需要的范围比较广, 为了一个上传图片已经折腾了多少知识点了, 还没完呢 …o(一︿一 +)o

回归正题, 说说几个要点, 首先看看代码的
document.getElementById("uploadImage") 出现了好几次了, 是不是好碍眼? 是不是好想优化? 是不是想为什么不把他放到一个变量存起来算了?
不行的, 因为当你存一个变量之后再上传文件, 你就找不到 FileReader 对象了, 详情可以参考一下我之前写的关於 Javascript 基本类型和引用类型小知识

然后 oFReader.onload 里面的 oFREvent 就是你能够拿到的数据了, 里面大概长这样子的, 看的眼花缭乱, 你们可以慢慢挖掘


其中:
readyState: 0- 还没有加载任何数据.1- 数据正在被加载.2- 已完成全部的读取请求.
result: 返回文件的内容。只有在读取操作完成后,此属性才有效,返回的数据的格式取决于是使用哪种读取方法来执行读取操作的。

中间插播一则消息吧, 图中可看到一个 result 属性的地址, 那是一个 base64 编码. 就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址。
我们所看到的网页上的每一个图片,都是需要消耗一个 http 请求下载而来的, 后面因此诞生的精灵图就是基于这个问题才出来, 不过局限性比较大, 很难适用到所有项目
详情可以参考一下【前端攻略】:玩转图片 Base64 编码

另外就是兼容问题, 因为懒得一个个去验证, 就没写上去, 不过查过资料放下来给你们, 可能有用到, 详情可以参考一下
对于 Chrome、Firefox、IE10 使用 FileReader 来实现。
对于 IE6~9 使用滤镜 filter:progid:DXImageTransform.Microsoft.AlphaImageLoader 来实现。

<!doctype html>
<html>

  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <title></title>
    <script type="text/javascript">
      var loadImageFile = (function () {if (window.FileReader) {
          var oPreviewImg = null,
            oFReader = new window.FileReader(),
            rFilter = /^(?:image\/bmp|image\/cis\-cod|image\/gif|image\/ief|image\/jpeg|image\/jpeg|image\/jpeg|image\/pipeg|image\/png|image\/svg\+xml|image\/tiff|image\/x\-cmu\-raster|image\/x\-cmx|image\/x\-icon|image\/x\-portable\-anymap|image\/x\-portable\-bitmap|image\/x\-portable\-graymap|image\/x\-portable\-pixmap|image\/x\-rgb|image\/x\-xbitmap|image\/x\-xpixmap|image\/x\-xwindowdump)$/i;

          oFReader.onload = function (oFREvent) {if (!oPreviewImg) {var newPreview = document.getElementById("imagePreview");
              oPreviewImg = new Image();
              newPreview.appendChild(oPreviewImg);
            }
            oPreviewImg.src = oFREvent.target.result;
          };

          return function () {
            var aFiles = document
              .getElementById("imageInput")
              .files;
            if (aFiles.length === 0) 
              return;
            if (!rFilter.test(aFiles[0].type)) {alert("You must select a valid image file!");
              return;
            }
            oFReader.readAsDataURL(aFiles[0]);
          }
        } else if (navigator.appName === "Microsoft Internet Explorer") {return function () {
            document
              .getElementById("imagePreview")
              .filters
              .item("DXImageTransform.Microsoft.AlphaImageLoader")
              .src = document
              .getElementById("imageInput")
              .value;
          }
        }
      })();
    </script>
    <style type="text/css">
      #imagePreview {filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale);
      }
    </style>
  </head>

  <body>
    <input id="imageInput" type="file" name="myPhoto" onchange="loadImageFile();"/>
    <div id="imagePreview"></div>
  </body>

</html>

没有 IE 浏览器测试,所以不知道是不是有效,其实里面看起来比我写的那个复杂,实际上多了个检验格式:

rFilter = /^(?:image\/bmp|image\/cis\-cod|image\/gif|image\/ief|image\/jpeg|image\/jpeg|image\/jpeg|image\/pipeg|image\/png|image\/svg\+xml|image\/tiff|image\/x\-cmu\-raster|image\/x\-cmx|image\/x\-icon|image\/x\-portable\-anymap|image\/x\-portable\-bitmap|image\/x\-portable\-graymap|image\/x\-portable\-pixmap|image\/x\-rgb|image\/x\-xbitmap|image\/x\-xpixmap|image\/x\-xwindowdump)$/i;

IE 上兼容写法:

document.getElementById("imagePreview").filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = document.getElementById("imageInput").value;

所以不要怕,如果我错了记得提醒下我啊。

目前为止其实已经该说的都差不多覆盖到了吧,动手能力强的话已经可以根据教程写一个实例出来的了。我看情况要不要加一个实战代码做系列结尾。

退出移动版