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


    转载至 : 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
  • 相关阅读:
    使用jquery.js写可增行删行可编辑的table
    当Ext.js中xtype: 'checkboxfield'时,没勾选则向后台发送的数据没有字段的解决方法
    沉迷js不能自拔~
    kubernetes concepts -- Pod Overview
    kubernetes concepts (一)
    所有锁的unlock要放到try{}finally{}里,不然发生异常返回就丢了unlock了
    Java故障定位方法总结
    年度计划
    minikube 设置CPU和内存
    Ubuntu 设置中文输入法
  • 原文地址:https://www.cnblogs.com/Gile/p/3693228.html
Copyright © 2020-2023  润新知