html5 文件系统File API
前言:
在做浏览器上传图片的时候,一般采用form表单上传,这种上传无法预览图片,无法查看图片大小,无法知道图片的类型等等!那么在html5 File API提供了这些表单无法实现的功能,而且还支持拖拽上传!现在我们开始学习。。
一、File Api 浏览器支持检测
File Api给js提供了以下几个接口来访问本地文件系统:
1、File - 单个文件;提供了诸如name、file size、mimetype(媒体类型)等只读文件属性。
2、FileList - 一个类数组File对象集合;
3、Blob - 文件对象的二进制原始数据;
File API还提供了一个异步读取文件的接口 - FileReader。
现在我们开始检测浏览器兼容情况:
- if(window.File && window.FileReader && window.FileList && window.Blob) {
- alert('您的浏览器支持所有的file api');
- } else {
- alert('您的浏览器不支持文件上传!请先升级您的浏览器!');
- }
File API主要是用来获取本地文件系统中文件的reference(引用对象),通过File API我们可以获得一个代表本地文件的js对象,而FileReader通过该File对象即可异步地读取本地文件的内容。
二、form表单的file控件
在html5中,file控件支持选择多个文件,用户选择了某些文件之后,html5为我们提供了一个访问这些文件的对象 - FileList,这是一个类数组集合,每一个元素为一个File对象,File对象中包含了文件的所有可访问信息。
示例的HTML代码如下:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>图片上传--多文件上传</title>
- </head>
- <body>
- <input type="file" id="Files" name="files[]" multiple />
- <div id="Lists"></div>
- <!-- multiple 属性支持多文件上传 -->
- </body>
- </html>
- <script>
- function fileSelect(e) {
- e = e || window.event;
- var files = e.target.files; //FileList 对象
- var output = [];
- for(var i = 0, f; f = files[i]; i++) {
- output.push('<li><strong>' + f.name + '</strong>(' + f.type + ') - ' + f.size +' bytes</li>');
- document.getElementById('Lists').innerHTML = '<ul>' + output.join('') + '</ul>';
- }
- }
- if(window.File && window.FileList && window.FileReader && window.Blob) {
- document.getElementById('Files').addEventListener('change', fileSelect, false);
- } else {
- document.write('您的浏览器不支持File Api');
- }
- </script>
由以上代码可以看到,html5为file这个dom元素新增了files接口(e.target指向了file input元素,实际上也可以用this来访问,即this.files),得到的就是FileList,通过遍历该集合,即可访问到各个已选择的文件对象。
三、Drag And Drop API
除了可以通过表单file控件访问本地文件外,还可以通过拖放API来访问。
html5还提供了一个更快捷的方式来触发读取文件的时机,前面我们已经说过,浏览器不能主动地访问本地操作系统,只能依赖于用户行为,用户想要访问文件时,才去访问本地文件系统。
拖放API能做的事情很多,在这里我只介绍一下它在访问本地文件方面的功能。
示例代码:
- function fileDrop(e) {
- e = e || window.event;
- e.stopPropagation(); // 阻止冒泡
- e.preventDefault(); //阻止默认行为
- var files = e.dataTransfer.files; //FileList
- var output = [];
- for(var i = 0, f; f = files[i]; i++) {
- output.push('<li><strong>' + f.name + '</strong>(' + f.type + ') - ' + f.size +' bytes</li>');
- }
- document.getElementById('Lists').innerHTML = '<ul>' + output.join('') + '</ul>';
- };
- function dragOver(e) {
- e = e || window.event;
- e.stopPropagation();
- e.preventDefault();
- e.dataTransfer.dropEffect = 'copy'; //指定拖放视觉效果
- };
- var d = document.getElementById('DropZone');
- try {
- d.addEventListener('dragover', dragOver, false);
- d.addEventListener('drop', fileDrop, false)
- } catch(ex) {
- document.write('something must be wrong!');
- }
由以上代码可以看到,拖放api是以event对象的dataTransfer对象作为数据载体,该对象具有一个files属性,通过该属性,可以拿到我们想要的FileList集合。后面将介绍FileReader,通过它具体地读取文件内容。
更多关于dataTransfer
拖拽事件
通过拖拽事件,咱们就可以控制拖拽很多东西了。其中什么元素或者是哪里发生了拖拽事件是最关键的。有些事件是在被拖动的元素上触发,有些事件是在放置目标上触发的。
拖动某元素时候,触发的事件有:dragstart事件、drag事件和dragend事件。
按下鼠标键并开始移动鼠标的时候,会在被拖拽的元素上触发dragstart事件。这时候光标变成”不能放”符号(圆环中有一条反斜线),表示不能把元素放在自己上门。拖拽开始时,可以通过ondragstart事件处理程序运行JavaScript代码。
触发dragstart事件后,随即会触发drag事件,而在元素被拖动期间会持续触发drag事件。这个事件与mousemove和touchmove事件类似。当拖动停止时(无论把元素放到了有效的放置目标,还是放到了无效的放置目标上),都会发生dragend事件。
上面说的三个事件的目标都是被拖动的元素触发。默认情况下,浏览器不会再拖动期间改变被拖动元素的外观。但是可以自行修改。不过,大多数浏览器会为正被拖动的元素创建一个半透明的副本,这个副本始终跟随光标移动。当某个元素被拖动到一个有效的放置
目标的时候,会触发的事件有:dragenter事件、dragover事件和dragleave或者drop事件。
只要有元素被拖动到放置目标上,就会触发dragenter事件(类似于mouseover事件)。紧随其后的是dragover事件,而且在被拖动的元素还在放置目标的范围内移动是,会连续触发dragover事件。如果元素被拖出放置目标,dragover事件不再发生,但是会触发
dragleave事件(类似于mouseout事件)。如果元素被放到了放置目标中会触发drop事件而不是dragleave事件。dragenter事件、dragover事件和dragleave或者drop事件的目标都是作为放置目标的元素。
读取文件
HTML5提供了一个叫FileReader的接口,用于异步读取文件内容,它主要定义了以下几个方法:
readAsBinaryString(File|Blob)
readAsText(File|Blob [, encoding])
readAsDataURL(File|Blob)
readAsArrayBuffer(File|Blob)
FileReader还提供以下事件:
onloadstart
onprogress
onload
onabort
onerror
onloadend
一旦调用了以上某个方法读取文件后,我们可以监听以上任何一个事件来获得进度、结果等。
预览本地图片
这里主要用到FileReader的readAsDataURL方法,通过将图片数据读取成Data URI的方法,将图片展示出来
- function fileSelect2(e) {
- e = e || window.event;
- var files = this.files;
- var p = document.getElementById('preview2');
- for(var i = 0, f; f = files[i]; i++) {
- var reader = new FileReader();
- reader.onload = (function(file) {
- return function(e) {
- var span = document.createElement('span');
- span.innerHTML = '<img style="padding: 0 10px;" width="100" src="'+ this.result +'" alt="'+ file.name +'" />';
- p.insertBefore(span, null);
- };
- })(f);
- //读取文件内容
- reader.readAsDataURL(f);
- }
- }
- document.getElementById('files2').addEventListener('change', fileSelect2, false);
完整代码如下:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>图片上传--预览</title>
- </head>
- <body>
- <p><input type="file" id="files2" accept="image/*" multiple=""></p>
- <div id="preview2"></div>
- </body>
- </html>
- <script type="text/javascript">
- (function() {
- function fileSelect2(e) {
- e = e || window.event;
- var files = this.files;
- var p = document.getElementById('preview2');
- for(var i = 0, f; f = files[i]; i++) {
- var reader = new FileReader();
- reader.onload = (function(file) {
- return function(e) {
- var span = document.createElement('span');
- span.innerHTML = '<img style="padding: 0 10px;" width="100" src="'+ this.result +'" alt="'+ file.name +'" />';
- p.insertBefore(span, null);
- };
- })(f);
- //读取文件内容
- reader.readAsDataURL(f);
- }
- }
- document.getElementById('files2').addEventListener('change', fileSelect2, false);
- })();
- </script>
PS:由于需要在页面上预览文本,所以则需要对文件中的html特殊字符进行实体编码,避免浏览器解析文件中的html代码。
监控读取进度
既然FileReader是异步读取文件内容,那么就应该可以监听它的读取进度。
事实上,FileReader的onloadstart以及onprogress等事件,可以用来监听FileReader的读取进度。
在onprogress的事件处理器中,有一个ProgressEvent对象,这个事件对象实际上继承了Event对象,提供了三个只读属性:
lengthComputable
loaded
total
通过以上几个属性,即可实时显示读取进度,不过需要注意的是,此处的进度条是针对单次读取的进度,即一次readAsBinaryString等方法的读取进度。
- var input4 = document.getElementById('file4');
- var bar = document.getElementById('progress-bar');
- var progress = document.getElementById('progress');
- function startHandler(e) {
- bar.style.display = 'block';
- }
- function progressHandler(e) {
- var percentLoaded = Math.round((e.loaded / e.total) * 100);
- if (percentLoaded < 100) {
- progress.style.width = percentLoaded + '%';
- progress.textContent = percentLoaded + '%';
- }
- }
- function loadHandler(e) {
- progress.textContent = '100%';
- progress.style.width = '100%';
- }
- function fileSelect4(e) {
- var file = this.files[0];
- if(!file) {
- alert('请选择文件!');
- return false;
- }
- if(file.size > 500 * 1024 * 1024) {
- alert('文件太大,请选择500M以下文件,防止浏览器崩溃!');
- return false;
- }
- progress.style.width = '0%';
- progress.textContent = '0%';
- var reader = new FileReader();
- reader.onloadstart = startHandler;
- reader.onprogress = progressHandler;
- reader.onload = loadHandler;
- reader.readAsBinaryString(this.files[0]);
- }
- input4.onchange = fileSelect4;
完整html代码如下:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>上传进度条</title>
- </head>
- <body>
- <p><input id="file4" type="file"></p>
- <div id="progress-bar" style="border:1px solid #333;padding:5px;color:#fff;display:none;">
- <div style="width:0%;height:100%;background:blue;white-space:nowrap;" id="progress"></div>
- </div>
- </body>
- </html>
- <script type="text/javascript">
- (function() {
- var input4 = document.getElementById('file4');
- var bar = document.getElementById('progress-bar');
- var progress = document.getElementById('progress');
- function startHandler(e) {
- bar.style.display = 'block';
- }
- function progressHandler(e) {
- var percentLoaded = Math.round((e.loaded / e.total) * 100);
- if (percentLoaded < 100) {
- progress.style.width = percentLoaded + '%';
- progress.textContent = percentLoaded + '%';
- }
- }
- function loadHandler(e) {
- progress.textContent = '100%';
- progress.style.width = '100%';
- }
- function fileSelect4(e) {
- var file = this.files[0];
- if(!file) {
- alert('请选择文件!');
- return false;
- }
- if(file.size > 500 * 1024 * 1024) {
- alert('文件太大,请选择500M以下文件,防止浏览器崩溃!');
- return false;
- }
- progress.style.width = '0%';
- progress.textContent = '0%';
- var reader = new FileReader();
- reader.onloadstart = startHandler;
- reader.onprogress = progressHandler;
- reader.onload = loadHandler;
- reader.readAsBinaryString(file);
- }
- input4.onchange = fileSelect4;
- })();
- </script>
分割文件
有的时候,一次性将一个大文件读入内存,并不是一个很好的选择(如果文件太大,可能直接导致浏览器崩溃),上述的监听进度示例就有可能在文件太大的情况下崩溃。
更稳健的方法是分段读取!
分段读取文件
HTML5 File Api提供了一个slice方法,允许分片读取文件内容。
- function readBlob(start, end) {
- var files = document.getElementById('file5').files;
- if(!files.length) {
- alert('请选择文件');
- return false;
- }
- var file = files[0],
- start = parseInt(start, 10) || 0,
- end = parseInt(end, 10) || (file.size - 1);
- var r = document.getElementById('range'),
- c = document.getElementById('content');
- var reader = new FileReader();
- reader.onloadend = function(e) {
- if(this.readyState == FileReader.DONE) {
- c.textContent = this.result;
- r.textContent = "Read bytes: " + (start + 1) + " - " + (end + 1) + " of " + file.size + " bytes";
- }
- };
- var blob;
- if(file.webkitSlice) {
- blob = file.webkitSlice(start, end + 1);
- } else if(file.mozSlice) {
- blob = file.mozSlice(start, end + 1);
- } else if(file.slice) {
- blob = file.slice(start, end + 1);
- }
- reader.readAsBinaryString(blob);
- };
- document.getElementById('file5').onchange = function() {
- readBlob(10, 100);
- }
html代码如下:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>分段读取文件</title>
- </head>
- <body>
- <input type="file" id="file5">
- <div id="range"></div>
- <div id="content"></div>
- </div>
- </body>
- </html>
- <script type="text/javascript">
- (function() {
- function readBlob(start, end) {
- var files = document.getElementById('file5').files;
- if(!files.length) {
- alert('请选择文件');
- return false;
- }
- var file = files[0],
- start = parseInt(start, 10) || 0,
- end = parseInt(end, 10) || (file.size - 1);
- var r = document.getElementById('range'),
- c = document.getElementById('content');
- var reader = new FileReader();
- reader.onloadend = function(e) {
- if(this.readyState == FileReader.DONE) {
- c.textContent = this.result;
- r.textContent = "Read bytes: " + (start + 1) + " - " + (end + 1) + " of " + file.size + " bytes";
- }
- };
- var blob;
- if(file.webkitSlice) {
- blob = file.webkitSlice(start, end + 1);
- } else if(file.mozSlice) {
- blob = file.mozSlice(start, end + 1);
- } else if(file.slice) {
- blob = file.slice(start, end + 1);
- }
- reader.readAsBinaryString(blob);
- };
- document.getElementById('file5').onchange = function() {
- readBlob(10, 100);
- }
- })();
- </script>
本例使用了FileReader的onloadend事件来检测读取成功与否,如果用onloadend则必须检测一下FileReader的readyState,因为read abort时也会触发onloadend事件,如果我们采用onload,则可以不用检测readyState。
分段读取进度
那分段读取一个大文件时,如何监控整个文件的读取进度呢?
这种情况下,因为我们调用了多次FileReader的文件读取方法,跟上文一次性把一个文件读到内存中的情况不大相同,不能用onprogress来监控。
我们可以监听onload事件,每次onload代表每个片段读取完毕,我们只需要在onload中计算已读取的百分比就可以了!
- var bar2 = document.getElementById('progress-bar2');
- var progress2 = document.getElementById('progress2');
- var input6 = document.getElementById('file6');
- var block = 1 * 1024 * 1024; // 每次读取1M
- // 当前文件对象
- var file;
- // 当前已读取大小
- var fileLoaded;
- // 文件总大小
- var fileSize;
- // 每次读取一个block
- function readBlob2() {
- var blob;
- if(file.webkitSlice) {
- blob = file.webkitSlice(fileLoaded, fileLoaded + block + 1);
- } else if(file.mozSlice) {
- blob = file.mozSlice(fileLoaded, fileLoaded + block + 1);
- } else if(file.slice) {
- blob = file.slice(fileLoaded, fileLoaded + block + 1);
- } else {
- alert('不支持分段读取!');
- return false;
- }
- reader.readAsBinaryString(blob);
- }
- // 每个blob读取完毕时调用
- function loadHandler2(e) {
- fileLoaded += e.total;
- var percent = fileLoaded / fileSize;
- if(percent < 1) {
- // 继续读取下一块
- readBlob2();
- } else {
- // 结束
- percent = 1;
- }
- percent = Math.ceil(percent * 100) + '%';
- progress2.innerHTML = percent;
- progress2.style.width = percent;
- }
- function fileSelect6(e) {
- file = this.files[0];
- if(!file) {
- alert('文件不能为空!');
- return false;
- }
- fileLoaded = 0;
- fileSize = file.size;
- bar2.style.display = 'block';
- // 开始读取
- readBlob2();
- }
- var reader = new FileReader();
- // 只需监听onload事件
- reader.onload = loadHandler2;
- input6.onchange = fileSelect6
运行此示例:(提示:请选择一个1G以上文件)
html源码:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>分段读取文件</title>
- </head>
- <body>
- <p><input type="file" id="file6"></p>
- <div id="progress-bar2" style="border:1px solid #333;padding:5px;color:#fff;display:none;">
- <div style="width:0%;height:100%;background:blue;white-space:nowrap;" id="progress2"></div>
- </div>
- </body>
- </html>
- <script type="text/javascript">
- (function() {
- var bar2 = document.getElementById('progress-bar2');
- var progress2 = document.getElementById('progress2');
- var input6 = document.getElementById('file6');
- var block = 1 * 1024 * 1024; // 1M
- var file;
- var fileLoaded;
- var fileSize;
- function readBlob2() {
- var blob;
- if(file.webkitSlice) {
- blob = file.webkitSlice(fileLoaded, fileLoaded + block + 1);
- } else if(file.mozSlice) {
- blob = file.mozSlice(fileLoaded, fileLoaded + block + 1);
- } else if(file.slice) {
- blob = file.slice(fileLoaded, fileLoaded + block + 1);
- } else {
- alert('不支持分段读取!');
- return false;
- }
- reader.readAsBinaryString(blob);
- }
- function loadHandler2(e) {
- fileLoaded += e.total;
- var percent = fileLoaded / fileSize;
- if(percent < 1) {
- // 继续读取下一块
- readBlob2();
- } else {
- // 结束
- percent = 1;
- }
- percent = Math.ceil(percent * 100) + '%';
- progress2.innerHTML = percent;
- progress2.style.width = percent;
- }
- function fileSelect6(e) {
- file = this.files[0];
- if(!file) {
- alert('文件不能为空!');
- return false;
- }
- fileLoaded = 0;
- fileSize = file.size;
- bar2.style.display = 'block';
- readBlob2();
- }
- var reader = new FileReader();
- reader.onload = loadHandler2;
- input6.onchange = fileSelect6
- })();
- </script>
注意事项
在chrome浏览器上测试时,如果直接以file://xxx这种形式访问demo,会出现FileReader读取不到内容的情况,表现为FileReader的result为空或者FileReader根本就没有去读取文件内容,FileReader各个事件没有触发;
这种情况我想应该是类似于chrome不允许添加本地cookie那样,chrome也不允许以file://xxx这种页面上的js代码访问文件内容;
解决办法很简单,只需要将测试文件放到一个web服务器上,以http://xxx形式访问即可。
参考网址:
- http://www.html5rocks.com/en/tutorials/file/dndfiles/
- http://hushicai.com/2014/03/29/html5-du-qu-ben-di-wen-jian.html
html5 文件系统File API的更多相关文章
- 【小月博客】用HTML5的File API做上传图片预览功能
前段时间做了一个项目,涉及到上传本地图片以及预览的功能,正好之前了解过 html5(点击查看更多关于web前端的有关资源) 可以上传本地图片,然后再网上看了一些demo结合自己的需求,终于搞定了.(P ...
- HTML5的File API读取文件信息
html结构: <div id="fileImage"></div> <input type="file" value=" ...
- HTML5 File api 实现断点续传
目前市场上大多数的网站的断点上传都是需要安装浏览器插件的,本文就针对高级浏览器的环境下,通过HTML5 File api实现断点上传进行说明 一.实现文件多选 HTML5的<input>新 ...
- 使用 JavaScript File API 实现文件上传
概述 以往对于基于浏览器的应用而言,访问本地文件都是一件头疼的事情.虽然伴随着 Web 2.0 应用技术的不断发展,JavaScript 正在扮演越来越重要的角色,但是出于安全性的考虑,JavaScr ...
- 通过Canvas及File API缩放并上传图片
原文地址:Resize an Image Using Canvas, Drag and Drop and the File API 示例地址:Canvas Resize Demo 原文作者:Dr. T ...
- 通过Canvas及File API缩放并上传图片完整演示样例
创建一个只管的用户界面,并同意你控制图片的大小.上传到server端的数据,并不须要处理enctype为 multi-part/form-data 的情况.只一个简单的POST表单处理程序就能够了. ...
- HTML5 File API
1.File API 一直以来,不能直接访问用户计算机中的文件都是web应用开发当中的一大障碍.File API的宗旨是为web开发人员提供一种安全的方式,以便在客户端访问用户计算机中的文件,并更好的 ...
- HTML5 File API的应用
HTML5 File API简介 HTML5File API协议族 Directories and System 文件系统和目录读取 FileWriter 写入文件 FileReader ...
- HTML5 File API — 让前端操作文件变的可能
前言 在 HTML5 File API 出现之前,前端对于文件的操作是非常有局限性的,大多需要配合后端实现.出于安全角度考虑,从本地上传文件时,代码不可能获取文件在用户本地的地址,所以纯前端不可能完成 ...
随机推荐
- JBPM4实例教程
JBPM语言概述:全名 Java Business Process Management ,它是覆盖了业务流程管理.工作流.服务协作等领域的一个开源的.灵活的.易扩展的可运行流程语言框架. 是开源 ...
- eclipse 在win7 64两个图标出现位操作系统无法锁定到任务栏或任务栏
eclipse 在win7 64位操作系统无法锁定到任务栏或者任务栏上出现两个图标 解决的方法 eclipse在win7 64bit下无法锁定到任务栏问题(或是锁定后任务栏出现两个eclipse图标) ...
- hibernate Java 时间和日期类型 Hibernate 制图
基础知识: 于 Java 于, 型表示的时间和日期包含: java.util.Date 和 java.util.Calendar. 外, 在 JDBC API 中还提供了 3 个扩展了 java.ut ...
- Base64中文不能加密问题
最近用到了Base64.js来对url参数进行加密,字母和数字都可以很好地加密/解密. 但测试中文时发现不能进行转换,貌似Base64.js不支持中文字符. 联想到encodeURI()对url的编码 ...
- Oracle 修改字符集
出现ORA-12899,是字符集引起的,中文在UTF-8中占3个字节,ZHS16GBK中占2个字节,而源dmp文件字符集是ZHS16GBK库里倒出来的数据,现在要导入到目标字符集为UTF-8的库里,所 ...
- HTTPS背后的加密算法(转)
当你在浏览器的地址栏上输入https开头的网址后,浏览器和服务器之间会在接下来的几百毫秒内进行大量的通信.InfoQ的这篇文章对此有非常详细的描述.这些复杂的步骤的第一步,就是浏览器与服务器之间协商一 ...
- Date、DateTime值的格式化扩展函数
public static class DateTimeExtensions { public static DateTime ToUtc(this DateTime time) ...
- Java迭代器[转]
迭代器是一种模式,它可以使得对于序列类型的数据结构的遍历行为与被遍历的对象分离,即我们无需关心该序列的底层结构是什么样子的.只要拿到这个对象,使用迭代器就可以遍历这个对象的内部. 1.Iterator ...
- java_log4j----java 日志管理
log4j 有三个主要组件: 类别Loggers------消息类型和优先级 附加目的地Appenders-------在哪里报告消息 布局Layouts------控制如何格式化消息 这三个组件共同 ...
- Javascript闭包的一些研究
原文:Javascript闭包的一些研究 本文不谈闭包的概念,因为概念容易把人搞晕,本文希望通过几个鲜活的例子来探究闭包的性质,相信对理解闭包会有所帮助. 程序1 var f = (function( ...