转载至 : http://www.360doc.com/content/14/0214/18/1457948_352511645.shtml

FileReader 资料(英文): https://developer.mozilla.org/en-US/docs/Web/API/FileReader#State_constants

前面本博介绍了File API,这里将继续介绍一下FileReader,用FileReader具体地读取文件内容。

         NOTE: 在chrome浏览器上本地测试的时候,即以file://xxx这种形式测试本文中的demo,会出现FileReader读取不到内容的情况,表现为FileReader的result为空或者FileReader根本就没有去读取文件内容,FileReader各个事件没有触发;这种情况我想应该是类似于chrome不允许添加本地cookie那样,chrome也不允许以file://xxx这种页面上的js代码访问文件内容;解决办法很简单,只需要将测试文件放到一个web服务器上,以http://xxx形式访问即可。

一、FileReader读取文件内容

File API一文中主要介绍获取文件句柄的方法,接下来我们就要利用该文件句柄来读取文件内容,这是通过FileReader来实现的,通过FileReader接口,我们可以异步地将文件内容加载到内存中,赋予某个js变量。

FileReader具体支持哪些方法和事件,这里就不介绍了,有兴趣的可以去w3c官网上看看FileReader介绍,这里主要介绍一下FileReader两个常见应用。

1、预览本地图片

这里主要用到FileReader的readAsDataURL方法,通过将图片数据读取成Data URL的方法,将图片展示出来,关于DATA URI

示例脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function fileSelect(e) {
    e = e || window.event;
     
    var files = e.target.files;  //FileList Objects    
    var ireg = /image\/.*/i,
        p = document.getElementById('Preview');
         
    var ul = document.getElementById('Errors');
    for(var i = 0, f; f = files[i]; i++) {
        if(!f.type.match(ireg)) {
            //设置错误信息
            var li = document.createElement('li');
            li.innerHTML = '<li>' + f.name +'不是图片文件.</li>';
             
            ul.appendChild(li);
             
            continue;
        }
         
        var reader = new FileReader();
         
        reader.onload = (function(file) {
            return function(e) {
                var span = document.createElement('span');
                span.innerHTML = '<img class="thumb" src="'+this.result +'" alt="'+ file.name +'" />';
                 
                p.insertBefore(span, null);
            };
        })(f);
        //读取文件内容
        reader.readAsDataURL(f);
    }
}
     
if(window.File && window.FileList && window.FileReader && window.Blob) {
    document.getElementById('Files').addEventListener('change', fileSelect, false);
else {
    document.write('您的浏览器不支持File Api');
}

由以上代码可知,调用FileReader的readAsDataURL接口,将启动异步加载文件内容,通过给reader监听一个onload事件,将数据加载完毕后,在onload事件处理中,通过reader的result属性即可获得文件内容,查看示例>>

        NOTE:在示例中,我给图片指定了一个height:75px的css样式,主要是为了让浏览器对图片进行等比缩放处理,所以在浏览器中展示出来的图片并不是原始大小的图片,而是经过浏览器自动等比缩放的图片;如果需要查看原始尺寸图片,可点击相应图片;再次单击该图片,则恢复小图片。

2、预览文本文件

这里主要用到FileReader的readAsText,对于诸如mimetype为text/plain、text/html等文件均认为是文本文件,即minetype为text开头都能在本例中预览。

NOTE:由于需要在页面上预览文本,如果使用innerHTML插入文本的话,则需要对html中一些特殊字符进行实体编码,这样才能保证正常显示文本。

简易的encodeHTML方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function encodeHTML(source) {
    return source
            .replace(/&/g, '&')
            .replace(/</g, '<')
            .replace(/>/g, '>')
            .replace(/"/, '"')
            .replace(/'/, ''');
};
function fileSelect(e) {
    e = e || window.event;
     
    var files = e.target.files;  //FileList Objects    
    var ireg = /text\/.*/i,
        p = document.getElementById('Preview');
         
    var ul = document.getElementById('Errors');
    for(var i = 0, f; f = files[i]; i++) {
        console.log(f.type);
        if(!f.type.match(ireg)) {
            //设置错误信息
            var li = document.createElement('li');
            li.innerHTML = '<li>' + f.name +'不是文本文件.</li>';
             
            ul.appendChild(li);
             
            continue;
        }
         
        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);
    }
}
     
if(window.File && window.FileList && window.FileReader && window.Blob) {
    document.getElementById('Files').addEventListener('change', fileSelect, false);
else {
    document.write('您的浏览器不支持File Api');
}

二、分段读取文件内容(slice)

有的时候,一次性将一个大文件读入内存,并不是一个很好的选择(如果文件太大,使用FileReader读取文件内容,可能直接导致浏览器崩溃),w3c也想到了这种情况,所以html5允许对文件进行分段读取。

chrome以及firefox已经将File slice api调整为如下:

1
2
3
4
5
6
7
var blob;
if(file.webkitSlice) {  //Blob中的方法
    blob = file.webkitSlice(start, end + 1,'text/plain;charset=UTF-8');
else if(file.mozSlice) {
    blob = file.mozSlice(start, end + 1, 'text/plain;charset=UTF-8');
}

本例使用了FileReader的onloadend事件来检测读取成功与否,如果用onloadend则必须检测一下FileReader readyState,因为read abort时也会触发onloadend事件,如果我们采用onload,则可以不用检测readyState。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function readBlob(start, end) {
    var files = document.getElementById('file').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中的方法
        blob = file.webkitSlice(start, end + 1,'text/plain;charset=UTF-8');
    else if(file.mozSlice) {
        blob = file.mozSlice(start, end + 1,'text/plain;charset=UTF-8');
    }
     
    reader.readAsBinaryString(blob);
};
try {
    document.getElementById('buttons').addEventListener('click',function(e) {
        if(e.target.tagName.toLowerCase() == 'button') {
            var start = e.target.getAttribute('data-start'),
                end = e.target.getAttribute('data-end');
                 
            readBlob(start, end);
        }  
    });
catch(ex) {
    alert('something error happens!')
}

NOTE:readAsBinaryString这个方法,读取的二进制字符串,在页面显示,出现中文乱码,不知道怎么解决,如果用reader.readAsText即可正常显示中文;在w3c官网上:binary string, in which every byte is represented by an integer in the range [0..255],而中文却不在[0...255]内,难道是因为这样才出现乱码?

三、FileReader进度条

既然FileReader是异步读取文件内容,那么就应该可以监听它的读取进度。事实上,FileReader的onloadstart以及onprogress等事件,可以用来监听FileReader的读取进度。

在onprogress的事件处理器中,提供了一个ProgressEvent对象,这个事件对象实际上继承了Event对象,提供了三个只读属性:lengthComputable、loaded、total;通过以上几个属性,即可实时显示读取进度。w3c官网上对它的定义如下:

1
2
3
4
5
interface ProgressEvent : Event {
  readonly attribute boolean lengthComputable;
  readonly attribute unsigned long long loaded;
  readonly attribute unsigned long long total;
};

如果处理的文件太大,可能会导致浏览器崩溃(chrome下一般都会崩溃掉,而firefox则不会,不过会触发FileReader的onerror事件,文件读取失败),所以为了安全地、正常地观察到文件读取进度,我们采用分段读取的方法来测试FileReader的进度条。

HTML代码如下:

1
2
3
4
5
6
7
8
9
10
11
<form>
    <fieldset>
        <legend>分度读取文件:</legend>
        <input type="file" id="File" />
        <input type="button" value="中断" id="Abort" />
        <p>
            <label>读取进度:</label><progress id="Progress"value="0" max="100"></progress>
        </p>
        <p id="Status"></p>
    </fieldset>
</form>

JS代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
var h = {
    init: function() {
        var me = this;
         
        document.getElementById('File').onchange = me.fileHandler;
        document.getElementById('Abort').onclick = me.abortHandler;
         
        me.status = document.getElementById('Status');
        me.progress = document.getElementById('Progress');
        me.percent = document.getElementById('Percent');
         
        me.loaded = 0;
        //每次读取1M
        me.step = 1024 * 1024;
        me.times = 0;
    },
    fileHandler: function(e) {
        var me = h;
         
        var file = me.file = this.files[0];
         
        var reader = me.reader = new FileReader();
         
        //
        me.total = file.size;
         
        reader.onloadstart = me.onLoadStart;
        reader.onprogress = me.onProgress;
        reader.onabort = me.onAbort;
        reader.onerror = me.onerror;
        reader.onload = me.onLoad;
        reader.onloadend = me.onLoadEnd;
        //读取第一块
        me.readBlob(file, 0);
    },
    onLoadStart: function() {
        var me = h;
    },
    onProgress: function(e) {
        var me = h;
         
        me.loaded += e.loaded;
        //更新进度条
        me.progress.value = (me.loaded / me.total) * 100;
    },
    onAbort: function() {
        var me = h;
    },
    onError: function() {
        var me = h;
         
    },
    onLoad: function() {
        var me = h;
        if(me.loaded < me.total) {
            me.readBlob(me.loaded);
        else {
            me.loaded = me.total;
        }
    },
    onLoadEnd: function() {
        var me = h;
         
    },
    readBlob: function(start) {
        var me = h;
         
        var blob,
            file = me.file;
         
        me.times += 1;
         
        if(file.webkitSlice) {
            blob = file.webkitSlice(start, start + me.step + 1);
        else if(file.mozSlice) {
            blob = file.mozSlice(start, start + me.step + 1);
        }
         
        me.reader.readAsText(blob);
    },
    abortHandler: function() {
        var me = h;
         
        if(me.reader) {
            me.reader.abort();
        }
    }
};
h.init();

例子中的进度条采用html5 progress元素来实现的。

每次读取1M的字节(你也可以随便改步长,比如说一次一个字节,然后传一个大小为几字节的文件,也能很好地观察到进度),在一次读取完毕后,onload事件中开启下一次读取,直至整个文件都读取完毕。

如果您的浏览器支持html5,您可以试一下:

分度读取文件:

读取进度:

这个示例中,没有限制文件大小,读取大文件时,也不会出现浏览器崩溃的情况,可以正常观察到文件的读取进度。

四、参考文章

3、Blob

HTML5 之 FileReader 的使用 (二) (网页上图片拖拽并且预显示可在这里学到) [转载]的更多相关文章

  1. HTML5 之 FileReader 的使用 (网页上图片拖拽并且预显示可在这里学到) [转载]

    转载至 : http://www.360doc.com/content/14/0214/18/1457948_352511416.shtml FileReader 资料(英文) : https://d ...

  2. FileReader (三) - 网页拖拽并预显示图片简单实现

    以下是一个很贱很简单的一个 在网页上图拽图片并预显示的demo. 我是从https://developer.mozilla.org/en-US/docs/Web/API/FileReader#Stat ...

  3. HTML5多图片拖拽上传带进度条

    前言 昨天利用css2的clip属性实现了网页进度条觉得还不错,但是很多情况下,我们在那些时候用进度条呢,一般网页加载的时候如果有需要可以用,那么问题就来了,怎么才算整个加载完毕呢,是页面主要模块加载 ...

  4. HTML5图片拖拽预览原理及实现

    一.前言 这两天恰好有一位同事问我怎样做一个图片预览功能.作为现代人的我们首先想到的当然是HTML5啦,其实HTML5做图片预览已经是一个老生常谈的问题了.我在这里就简单说说其中相关的一些东西,当然会 ...

  5. canvas 图片拖拽旋转之二——canvas状态保存(save和restore)

    引言 在上一篇日志“canvas 图片拖拽旋转之一”中,对坐标转换有了比较深入的了解,但是仅仅利用坐标转换实现的拖拽旋转,会改变canvas坐标系的状态,从而影响画布上其他元素的绘制.因此,这个时候需 ...

  6. javascript小实例,PC网页里的拖拽

    几年前,我参与设计开发一个房产网的项目,我负责前端工作,由于项目经理要求比较高,参考了很多房产类网站比较优秀的功能,想把别人比较优秀的设计和想法集合到一起,那时的设计稿和功能实现,简直就是改了又改,今 ...

  7. .net core版 文件上传/ 支持批量上传,拖拽以及预览,bootstrap fileinput上传文件

    asp.net mvc请移步 mvc文件上传支持批量上传,拖拽以及预览,文件内容校验 本篇内容主要解决.net core中文件上传的问题  开发环境:ubuntu+vscode 1.导入所需要的包:n ...

  8. 通过 JS 实现简单的拖拽功能并且可以在特定元素上禁止拖拽

    前言 关于讲解 JS 的拖拽功能的文章数不胜数,我确实没有必要大费周章再写一篇重复的文章来吸引眼球.本文的重点是讲解如何在某些特定的元素上禁止拖拽.这是我在编写插件时遇到的问题,其实很多插件的拖拽功能 ...

  9. HTML5——将图片拖拽上传

    如下图所示: 代码如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

随机推荐

  1. WPF进阶技巧和实战09-事件(1-路由事件、鼠标键盘输入)

    理解路由事件 当有意义的事情发生时,有对象(WPF的元素)发送的用于通知代码的消息,就是事件的核心思想.WPF通过事件路由的概念增强了.NET事件模型.事件由允许源自某个元素的事件由另一个元素引发.例 ...

  2. KMP算法,看这篇就够了!

    普通的模式匹配算法(BF算法) 子串的定位操作通常称为模式匹配算法 假设有一个需求,需要我们从串"a b a b c a b c a c b a b"中,寻找内容为"a ...

  3. 使用Charles 弱网测试

    打开Charles->Proxy→Throttle Settings 1.可以选择不通的网络类型,对于网络的配置一般修改下上行下行即可 2.网络设置各字段解释 bandwidth -- 带宽,即 ...

  4. [python]基于windows搭建django项目

    1.首先我的环境用到的库版本如下,若下载直接pip即可 pip3 install Django==2.0.6pip3 install djangorestframework==3.8.2pip3 in ...

  5. mysql查询报错this is incompatible with sql_mode=only_full_group_by

    临时改法:select @@GLOBAL.sql_mode;查询当前mysql的模式去掉ONLY_FULL_GROUP_BY重新设置:set @@GLOBAL.sql_mode='STRICT_TRA ...

  6. 微信小程序(九)

    小程序运行环境与基本架构 每个小程序都是运行在它所在的微信客户端上的,通过微信客户端给它提供的运行环境,小程序可以直接获取微信客户端的原生体验和原生能力. wxml视图文件和wxss样式文件都是对渲染 ...

  7. python中整除后结果也是小数

    有人这么回答,这显然不对 先看个例子: '//'明明是整除,为什么结果不是整数,而会出现小数? 首先,关于除法有三种概念:传统除法.精确除法和地板除 #1.传统除法:整数相除结果是整数,浮点数相除结果 ...

  8. 简易发号SQL,可用于生成指定前缀自增序列--改进版

    使用merge语法实现新增or更新 首先创建表 CREATE TABLE Test.dbo.Increments ( Prefix varchar(50) NOT NULL, [MaxNum ] bi ...

  9. [loj6051]PATH

    (不妨将下标改为从1开始) 参考loj2265中关于杨表的相关知识 构造一个$n$行且第$i$行有$a_{i}$个格子的杨表,依次记录其每一次增加的时间(范围为$[1,\sum_{i=1}^{n}a_ ...

  10. maven私服-仓库图