H5读取本地文件操作

本文转自:转:http://hushicai.com/2014/03/29/html5-du-qu-ben-di-wen-jian.html感谢大神分享。

常见的语言比如php、shell等,是如何读取文件的呢?

实际上,大多数语言都需要先获取文件句柄,然后调用文件访问接口,打开文件句柄,读取文件!

那么,HTML5是否也是这样的呢?

答案是肯定的!

HTML5为我们提供了一种与本地文件系统交互的标准方式:File Api

该规范主要定义了以下数据结构:

  • File
  • FileList
  • Blob

HTML5访问本地文件系统时,需要先获取File对象句柄,怎么获取文件引用句柄呢?

选择文件

首先检测一下当前浏览器是否支持File Api

function isSupportFileApi() {
if(window.File && window.FileList && window.FileReader && window.Blob) {
return true;
}
return false;
}

HTML5虽然可以让我们访问本地文件系统,但是js只能被动地读取,也就是说只有用户主动触发了文件读取行为,js才能访问到File Api,这通常发生在表单选择文件或者拖拽文件

表单输入

表单提交文件是最常见的场景,用户选择文件后,触发了文件选择框的change事件,通过访问文件选择框元素的files属性可以拿到选定的文件列表。

如果文件选择框指定了multiple,则一个文件选择框可以同时选择多个文件,files包含了所有选择的文件对象;如果没有指定,则只能选择一个文件,files[0]就是所选择的文件对象。

function fileSelect1(e) {
var files = this.files;
for(var i = 0, len = files.length; i < len; i++) {
var f = files[i];
html.push(
'<p>',
f.name + '(' + (f.type || "n/a") + ')' + ' - ' + f.size + 'bytes',
'</p>'
);
}
document.getElementById('list1').innerHTML = html.join('');
}
document.getElementById('file1').onchange = fileSelect1;

拖拽

拖拽是另一种常见的文件访问场景,这种方式通过一个叫dataTransfer的接口来获得拖拽的文件列表,更多关于dataTransfer

拖拽同样支持多选,用户可以拖拽多个文件。

function dropHandler(e) {
e.stopPropagation();
e.preventDefault(); var files = e.dataTransfer.files;
for(var i = 0, len = files.length; i < len; i++) {
var f = files[i];
html.push(
'<p>',
f.name + '(' + (f.type || "n/a") + ')' + ' - ' + f.size + 'bytes',
'</p>'
);
}
document.getElementById('list2').innerHTML = html.join('');
}
function dragOverHandler(e) {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dragEffect = 'copy';
}
var drag = document.getElementById('drag');
drag.addEventListener('drop', dropHandler, false);
drag.addEventListener('dragover', dragOverHandler, false);

PS:
拖拽有个特别需要注意的事情就是,阻止事件冒泡和事件默认行为,防止浏览器自动打开文件等行为,比如拖拽一个pdf,浏览器可能会打开pdf。

至此,我们知道,我们可以通过两种方式来获得文件句柄,那么如何读取文件内容呢?

读取文件

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);

调用FileReader的readAsDataURL接口时,浏览器将异步读取文件内容,通过给FileReader实例监听一个onload事件,数据加载完毕后,在onload事件处理中,通过reader的result属性即可获得文件内容。

预览文本文件

这里主要用到FileReader的readAsText,对于诸如mimeType为text/plain、text/html等文件均认为是文本文件,即mineType为text开头都可以用这个方法来预览。

function fileSelect3(e) {
e = e || window.event; var files = this.files;
var p = document.getElementById('preview3'); for(var i = 0, f; f = files[i]; i++) {
var reader = new FileReader();
reader.onload = (function(file) {
return function(e) {
var div = document.createElement('div');
div.className = "text"
div.innerHTML = encodeHTML(this.result); p.insertBefore(div, null);
};
})(f);
//读取文件内容
reader.readAsText(f);
}
}
document.getElementById('files3').addEventListener('change', fileSelect3, false);

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;

分割文件

有的时候,一次性将一个大文件读入内存,并不是一个很好的选择(如果文件太大,可能直接导致浏览器崩溃),上述的监听进度示例就有可能在文件太大的情况下崩溃。

更稳健的方法是分段读取!

分段读取文件

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);
}

本例使用了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

注意事项

在chrome浏览器上测试时,如果直接以file://xxx这种形式访问demo,会出现FileReader读取不到内容的情况,表现为FileReader的result为空或者FileReader根本就没有去读取文件内容,FileReader各个事件没有触发;

这种情况我想应该是类似于chrome不允许添加本地cookie那样,chrome也不允许以file://xxx这种页面上的js代码访问文件内容;

解决办法很简单,只需要将测试文件放到一个web服务器上,以http://xxx形式访问即可。

H5读取本地文件操作的更多相关文章

  1. Storm 实时读取本地文件操作(模拟分析网络日志)

    WebLogProduct 产生日志类 package top.wintp.weblog; import java.io.FileNotFoundException; import java.io.F ...

  2. 【转】flash air中读取本地文件的三种方法

    actionscript中读取本地文件操作有两种代码如下 1.使用File和FileStream两个类,FileStream负责读取数据的所以操作:(同步操作) var stream:FileStre ...

  3. FileReader读取本地文件

    FileReader是一种异步读取文件机制,结合input:file可以很方便的读取本地文件. 一.input:type[file] file类型的input会渲染为一个按钮和一段文字.点击按钮可打开 ...

  4. .NET 读取本地文件绑定到GridViewRow

    wjgl.aspx.cs: using System; using System.Collections; using System.Configuration; using System.Data; ...

  5. 【Egret】实现web页面操作PC端本地文件操作

    Egret 实现web页面操作PC端本地文件操作: http://edn.egret.com/cn/book/page/pid/181 //------------------------------ ...

  6. python 读取本地文件批量插入mysql

    Uin_phone.txt 本地文件内容 有1000条,这里只是展示前几条,供参考 133584752 133584759 133584764 133584773 133584775 13358477 ...

  7. HTML5 本地文件操作之FileSystemAPI实例(三)

    文件夹操作demo 1.读取根目录文件夹内容 window.requestFileSystem = window.requestFileSystem || window.webkitRequestFi ...

  8. HTML5 本地文件操作之FileSystemAPI实例(二)

    文件操作实例整理二 1.删除文件.复制文件.移动文件 //获取请求权限 window.requestFileSystem = window.requestFileSystem || window.we ...

  9. HTML5 本地文件操作之FileSystemAPI实例(一)

    文件操作实例整理一 1.请求系统配额类型 console.info(window.TEMPORARY); //0 临时 console.info(window.PERSISTENT); //1 持久 ...

随机推荐

  1. 数据库连接之SQL JDBC

    数据库连接之SQL JDBC SQlServer的配置: 1).外围配置服务器中将远程连接设置为:同时使用TCP/IP和named pipes 2).点击该连接->属性->安全性-> ...

  2. Scrapy的debug方式

    Scrapy不方便调试,但是为了深入学习框架内部的一些原理,有时候仅仅依靠日志是不够的.下面提供一种scrapy的debug方式 demo直接用来自官方例子来演示:https://github.com ...

  3. Asp.Net Core轻量级Aop解决方案:AspectCore

    什么是AspectCore Project ? AspectCore Project 是适用于Asp.Net Core 平台的轻量级 Aop(Aspect-oriented programming) ...

  4. Java File类应用:递归遍历文件夹和递归删除文件

    要求: 1)采用递归遍历文件夹下的所有文件,包括子文件夹下的文件 2)采用递归删除文件下的所有文件 注意: 以下递归删除文件的方法,只能删除文件,所有的文件夹都还会存在 若要删除正文文件夹,可以在递归 ...

  5. js扩展父类方法

    在网上找了很多一直没找到关于JS扩展父类的方法,让我很是郁闷啊~要是真的开发组遇到了该咋整,于是乎自己手写了一些测试代码,没想到通过了……(难道是人品太好了?)废话不多说了直接上代码看看~ <s ...

  6. charles连接手机抓包

    写给我自己: 如果是使用charles抓包.一定要tm的保证手机和电脑连的是一个网. 1.proxy setting,查看charles,端口 2.勾选 3.ipconfig,查看自己电脑的ip地址 ...

  7. 使用jedis实现Redis消息队列(MQ)的发布(publish)和消息监听(subscribe)

    前言: 本文基于jedis 2.9.0.jar.commons-pool2-2.4.2.jar以及json-20160810.jar 其中jedis连接池需要依赖commons-pool2包,json ...

  8. 扔掉log4j、log4j2,自己动手实现一个多功能日志记录框架,包含文件,数据库日志写入,实测5W+/秒日志文件写入,2W+/秒数据库日志写入,虽然它现在还没有logback那么强大

    讲到log4j,现在国外基本是没有开发者用这个框架了,原因大致有几点,1.功能太少:2.效率低下:3.线程锁bug等等等各种莫名其妙的bug一直都没解决. 其实最重要的是log4j的作者自己也放弃了l ...

  9. 宿主机共享文件夹给不同Linux虚拟机的方法

    一.Windows/Linux宿主机共享文件夹给VMWare中的Linux虚拟机 1.能安装vmware tools1)在vmware的ubuntu中安装vmware tools2)在vmware中开 ...

  10. [0] 分析 EntityName 时出错。 行 2,位置 *。

    1. 报错内容“若要在加载设计器前避免可能发生的数据丢失,必须纠正以下错误: ”   “分析 EntityName 时出错. 行 2,位置 *.” 2. 如图: 3. 解决方案:查看项目全路径,是否有 ...