注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

谈笑涧

已开通独立前端博客,请访问 http://www.candoudou.com

 
 
 

日志

 
 

JavaScript使用File APIs读取文件  

2012-04-20 09:59:28|  分类: html5+css3 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

介绍(introduction)

HTML5最终提供了与本地文件交互的标准方法:File API规范。正如文章示例中的功能,File API在图像未发送到服务器之前可以创建图像的缩略图预览,用户在离线状态下允许应用保存文件的引用。此外,你可以使用客户端的逻辑来验证文件的扩展名是否匹配上传的mimetype类型,限制上传文件的大小。

这份规范提供了几个访问本地文件系统的接口:

  1. File - 单个文件;提供只读的信息,例如:文件大小,mime类型,文件句柄的引用
  2. FileList - 类似数组的File对象序列。(想像<input type="file" multiple> 或从桌面文件夹中拖动的文件)。
  3. Blob - 允许把文件切片成字节范围。

当与上面的数据结构结合使用的时候,FileReader接口可以通过类似JS事件处理的方式异步读取文件。因此,它可以监视文件的读取进度,捕捉错误,载入完成时如何处理。在许多方面APIs工作的方式类似XMLHttpRequest的事件模型。

注意:在这个教程写下的此时,最低限度支持与本地文件交互的APIs的浏览器有:Chrome 6.0、Firefox 3.6。Firefox 3.6.3版本的浏览器没有支持File.slice()方法。

选择文件

在开始其它事情之前要做的第一件事是:检查浏览器是否充分支持File API:

// 检测不同的File API支持度 if (window.File && window.FileReader && window.FileList && window.Blob) {   // 支持所有的File API } else {   alert('The File APIs are not fully supported in this browser.'); } 

当然,如果你的app只用到其中的几个API,可以根据需求修改上面的代码。

使用表单方式选择文件

加载文件最直接的方法是使用标准的<input type="file">元素。JavaScript返回选中文件对象的列表:FileList。下面的例子使用'multiple'属性允许一次选中多个文件:

<input type="file" id="files" name="files[]" multiple /> <output id="list"></output>  <script>   function handleFileSelect(evt) {     var files = evt.target.files; // FileList对象       // files is a FileList of File objects. List some properties.     var output = [];     for (var i = 0, f; f = files[i]; i++) {       output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',                   f.size, ' bytes, last modified: ',                   f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',                   '</li>');     }     document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';   }    document.getElementById('files').addEventListener('change', handleFileSelect, false); </script> 

示例:使用表单方式选择文件。

  • static.bat (n/a) - 107 bytes, last modified: Friday, April 13, 2012
  • %u672C%u5730.bat (n/a) - 117 bytes, last modified: Friday, April 13, 2012

使用拖拽方式选择文件

另外一个加载文件的技术是原生的拖拽,可以从桌面拖动文件到浏览器。我们可以对前面的示例略作修改来添加拖拽支持。

<div id="drop_zone">Drop files here</div> <output id="list"></output>  <script>   function handleFileSelect(evt) {     evt.stopPropagation();     evt.preventDefault();      var files = evt.dataTransfer.files; // FileList object.      // files is a FileList of File objects. List some properties.     var output = [];     for (var i = 0, f; f = files[i]; i++) {       output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',                   f.size, ' bytes, last modified: ',                   f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',                   '</li>');     }     document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';   }    function handleDragOver(evt) {     evt.stopPropagation();     evt.preventDefault();     evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.   }    // Setup the dnd listeners.   var dropZone = document.getElementById('drop_zone');   dropZone.addEventListener('dragover', handleDragOver, false);   dropZone.addEventListener('drop', handleFileSelect, false); </script> 

示例:使用拖拽选择文件。

拖动文件到这儿

注意:一些浏览器会把<input type="file">元素当成终止拖放的目标(Some browsers treat <input type="file"> elements as native drop targets)。尝试拖动文件到前面示例的输入区域。

读取文件

现在到了有趣的部分!

在你获取File引用之后,实例化一个FileReader对象,可将文件内容读入到内存。当装载完成,会触发reader的onload事件,并且可以通过它的result属性访问文件数据。

FileReader提供四个方法异步地读取文件。

  • FileReader.readAsBinaryString(Blob|File) - result属性将包含类似二进制字符串的file/blob数据。每个字节代表在[0..255]之间的一个整数。
  • FileReader.readAsText(Blob|File, opt_encoding) - result属性将包含类似文本字符串的file/blob数据。默认的字符串是'UTF-8'解码。使用可选的编码参数来指定不同的格式。
  • FileReader.readAsDataURL(Blob|File) - result属性将包含把file/blob数据通过data URL编码后的值
  • FileReader.readAsArrayBuffer(Blob|File) - result属性将把file/blob数据作为ArrayBuffer对象返回。

一旦调用了FileReader的任何一个读取方法,onloadstartonprogressonloadonabortonerror, and onloadend都可以用来跟踪文件读取的进展情况。

这个下面的示例,过滤用户选取的图片,调用reader.readAsDataURL(),并通过设置缩略图的'src'属性来生成预览。

<style>   .thumb {     height: 75px;     border: 1px solid #000;     margin: 10px 5px 0 0;   } </style>  <input type="file" id="files" name="files[]" multiple /> <output id="list"></output>  <script>   function handleFileSelect(evt) {     var files = evt.target.files; // FileList object      // Loop through the FileList and render image files as thumbnails.     for (var i = 0, f; f = files[i]; i++) {        // Only process image files.       if (!f.type.match('image.*')) {         continue;       }        var reader = new FileReader();        // Closure to capture the file information.       reader.onload = (function(theFile) {         return function(e) {           // Render thumbnail.           var span = document.createElement('span');           span.innerHTML = ['<img class="thumb" src="', e.target.result,                             '" title="', escape(theFile.name), '"/>'].join('');           document.getElementById('list').insertBefore(span, null);         };       })(f);        // Read in the image file as a data URL.       reader.readAsDataURL(f);     }   }    document.getElementById('files').addEventListener('change', handleFileSelect, false); </script> 

示例:读取文件!

使用图片来测试该示例


分割文件

在一些情况下,把整个文件读到内存中并不是最好的选择。例如,你想写一个异步上传文件的程序。一个可行的方式是,把文件分割成byte文件块来发送文件。服务器组件可以以正确的顺序重建文件的内容。

幸运的是,File接口提供了分割的方法。该方法接收开始的byte值作为第一个参数,结束的byte值作为第二个参数,可选的内容类型作为第三个参数。由于这个方法的语义目前处于变动状态,所以它拥有一个开发商的前缀。

if (file.webkitSlice) {   var blob = file.webkitSlice(startingByte, endindByte); } else if (file.mozSlice) {   var blob = file.mozSlice(startingByte, endindByte); } reader.readAsBinaryString(blob); 

下面的示例演示读取文件块。一些值得注意的是,它使用onloadend事件和检查evt.target.readyState来代替使用onload事件。

<style>   #byte_content {     margin: 5px 0;     max-height: 100px;     overflow-y: auto;     overflow-x: hidden;   }   #byte_range { margin-top: 5px; } </style>  <input type="file" id="files" name="file" /> Read bytes:  <span class="readBytesButtons">   <button data-startbyte="0" data-endbyte="4">1-5</button>   <button data-startbyte="5" data-endbyte="14">6-15</button>   <button data-startbyte="6" data-endbyte="7">7-8</button>   <button>entire file</button> </span> <div id="byte_range"></div> <div id="byte_content"></div>  <script>   function readBlob(opt_startByte, opt_stopByte) {      var files = document.getElementById('files').files;     if (!files.length) {       alert('Please select a file!');       return;     }      var file = files[0];     var start = parseInt(opt_startByte) || 0;     var stop = parseInt(opt_stopByte) || file.size - 1;      var reader = new FileReader();      // If we use onloadend, we need to check the readyState.     reader.onloadend = function(evt) {       if (evt.target.readyState == FileReader.DONE) { // DONE == 2         document.getElementById('byte_content').textContent = evt.target.result;         document.getElementById('byte_range').textContent =              ['Read bytes: ', start + 1, ' - ', stop + 1,              ' of ', file.size, ' byte file'].join('');       }     };      if (file.webkitSlice) {       var blob = file.webkitSlice(start, stop + 1);     } else if (file.mozSlice) {       var blob = file.mozSlice(start, stop + 1);     }     reader.readAsBinaryString(blob);   }      document.querySelector('.readBytesButtons').addEventListener('click', function(evt) {     if (evt.target.tagName.toLowerCase() == 'button') {       var startByte = evt.target.getAttribute('data-startbyte');       var endByte = evt.target.getAttribute('data-endbyte');       readBlob(startByte, endByte);     }   }, false); </script> 

示例: 切片文件!

 Read bytes: 

监测读取进度

使用异步事件处理,我们可以去监测文件的读取进度;这对于大的文件来说十分有用处,也可以捕捉错误信息,当读取完成时作何处理。

事件onloadstartonprogress可以用来监视文件的读取进度。

下面的示例演示,使用进度条来监视读取状态。进度条指示文件读取进度,试着从你的硬盘中选择一个或多个文件。

<style>   #progress_bar {     margin: 10px 0;     padding: 3px;     border: 1px solid #000;     font-size: 14px;     clear: both;     opacity: 0;     -moz-transition: opacity 1s linear;     -o-transition: opacity 1s linear;     -webkit-transition: opacity 1s linear;   }   #progress_bar.loading {     opacity: 1.0;   }   #progress_bar .percent {     background-color: #99ccff;     height: auto;     width: 0;   } </style>  <input type="file" id="files" name="file" /> <button onclick="abortRead();">Cancel read</button> <div id="progress_bar"><div class="percent">0%</div></div>  <script>   var reader;   var progress = document.querySelector('.percent');    function abortRead() {     reader.abort();   }    function errorHandler(evt) {     switch(evt.target.error.code) {       case evt.target.error.NOT_FOUND_ERR:         alert('File Not Found!');         break;       case evt.target.error.NOT_READABLE_ERR:         alert('File is not readable');         break;       case evt.target.error.ABORT_ERR:         break; // noop       default:         alert('An error occurred reading this file.');     };   }    function updateProgress(evt) {     // evt is an ProgressEvent.     if (evt.lengthComputable) {       var percentLoaded = Math.round((evt.loaded / evt.total) * 100);       // Increase the progress bar length.       if (percentLoaded < 100) {         progress.style.width = percentLoaded + '%';         progress.textContent = percentLoaded + '%';       }     }   }    function handleFileSelect(evt) {     // Reset progress indicator on new file selection.     progress.style.width = '0%';     progress.textContent = '0%';      reader = new FileReader();     reader.onerror = errorHandler;     reader.onprogress = updateProgress;     reader.onabort = function(e) {       alert('File read cancelled');     };     reader.onloadstart = function(e) {       document.getElementById('progress_bar').className = 'loading';     };     reader.onload = function(e) {       // Ensure that the progress bar displays 100% at the end.       progress.style.width = '100%';       progress.textContent = '100%';       setTimeout("document.getElementById('progress_bar').className='';", 2000);     }      // Read in the image file as a binary string.     reader.readAsBinaryString(evt.target.files[0]);   }    document.getElementById('files').addEventListener('change', handleFileSelect, false); </script> 

示例: 监测文件读取的进度。

100%

提示: 为了更好的看到进度指示效果,从你的硬盘中选择比较大的文件。

资源

  评论这张
 
阅读(1064)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017