基于HTML5的移动Web应用——文件操作

过去Web程序不能替代桌面程序的一个重要原因就在于浏览器对于文件操作API的缺失。照片处理中的裁剪、滤镜,二维码的读取与识别,文档的查看和编辑等,这些操作无一不依赖文件的操作,HTML5赋予了浏览器几乎和本地程序同等强大的文件操作能力。

File API是HTML5在DOM标准中添加的功能,它允许Web内容在用户授权的情况下选择本地文件并读取内容一通过 File,FileList 和FileReader等对象共同作用来实现。

选择文件

1、通过表单选择文件

由于Web环境的特殊性,为了考虑文件安全问题,浏览器不允许JavaScript直接访问文件系统,但可以通过fle类型的input元素或者拖放的方式选择文件操作。

<input type="file" id="thisFile"/>

File表单可以让用户选取一个或者多个文件(multiple 属性),通过FileAPI,可在用户选择文件后访问到代表了所选文件列表的FileList对象,FileList 对象是一个类数组的对象,其中包含着一个或多个File对象。如果没有multiple属性或者用户只选了一个文件,那么只需要访问FileList对象的第一个元素:

var filelist=document.getElementById('thisFile') .files;
var selectedFile-filelist[0]; 

使用input元素时,用户在选择文件后会触发其change事件:

var inputElement=document.getElementById('thisFile')
inputElement. addEventListener ("change",handleFiles, false)
function handleFiles(){
     var fileList=this.files 
}

和其他类数组对象一样,FileList 也有length属性,可以轻松遍历其File对象:

for (var i=0, numFiles=files.length ; i< numFiles; i++) {
    var file=files[i]
    ……
}

File对象有3个很有用的属性,包括了关于该文件的许多有用信息。
(1) name: 文件名,不包含路径信息。
(2) size: 文件大小,以B为单位。
(3) type:文件的MIME type。
需要注意的是,这3个属性都是只读的。

2、通过拖曳来选择文件

使用拖曳的方式来选择文件的效果,需要通过访问dataTransfer的files属性来实现。

下面通过一个案例来演示具体效果,如示例所示。

【示例】 使用拖曳的方式选择文件

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<style>
    .dropzone{
        width: 200px;
        height: 100px;
        border: 2px  dashed #ddd;
        text-align: center;
        padding-top: 100px;
        color: #999;
    }
</style>
<body>
<div id="dropzone" class="dropzone">
    拖曳文件到此处
</div>
<div id="output" class="output">

</div>
<script>
    function getFileInfo(file) {
        var aMultiples = ["KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], sizeinfo;
        var info = '文件名:' + file.name ;
        // 计算文件大小的近似值
        for (var nMultiple = 0, nApprox = file.size / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {
            sizeinfo = nApprox.toFixed(3) + " " + aMultiples[nMultiple] + " (" + file.size + " bytes)";
        }
        info += ";大小:" + sizeinfo
        info += ";类型:" + file.type

        return info + '<br>'
    }

    var dropzone = document.getElementById('dropzone');
    dropzone.addEventListener('drop', function (e) {
        var html = '您一共选择了 ' + e.dataTransfer.files.length + '个文件,文件信息如下:<br>';
        [].forEach.call(e.dataTransfer.files, function (file) {
            html += getFileInfo(file);
        });
        document.getElementById('output').innerHTML = html
        e.preventDefault();
        e.stopPropagation();
    }, false);
    dropzone.addEventListener('dragover', function (e) {
        if (e.preventDefault) {
            // 必须要阻止dragover的默认行为(即不可drop),这样才能进行drop操作。
            // 否则不会触发drop事件
            e.preventDefault();
        }
        return false
    }, false)
</script>
</body>
</html>

用Chrome浏览器访问示例。

操作文件

1、FileReader 对象

前面讲到表单或者dataTansier对象中的File类型的实例代表着这个文件,但是这个文件对象只能访问到一些基本的信息(大小和文件名等),如果要访问文件的具体内容,还得借助FileReader对象。

FileReader对象可以将文件对象转换为字符串、DataURL对象或者二进制字符串等,以进行进一步操作。例如,在做图片上传功能时,可以先对选择的图片进行预览或者剪裁,待用户确认无误后再进行上传,可以节省许多不必要的带宽。以前文的拖曳文件例子为基础,加上拖曳图片预览功能,代码如示例所示。

【示例】 FileReader对象

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<style>
    .dropzone {
        width: 200px;
        height: 100px;
        border: 2px dashed #ddd;
        text-align: center;
        padding-top: 100px;
        color: #999;
    }
    .preview img {
        width: 100px;
        height: 100px;
    }
</style>
<body>
<div id="dropzone" class="dropzone">
    拖拽文件到此处
</div>
<div id="preview" class="preview">

</div>
<script>
    function handleFiles(files) {
        var preview = document.getElementById('preview')
        for (var i = 0; i < files.length; i++) {
            var file = files[i]
            // 用来过滤非图片类型
            var imageType = /image.*/

            if (!file.type.match(imageType)) {
                continue
            }
            // 只能动态创建img对象来进行预览
            var img = document.createElement("img")
            // 将文件对象存起来
            img.file = file
            // 新建 FileRead 对象——是不是很简单?
            var reader = new FileReader()
            // FileReader在读取文件时是异步执行的(JS中许多对象都有类似API),因此需
            要通过绑定其load事件来访问文件读取的结果
            // 要注意,这里使用了闭包,因为img只保存当前函数(handleFiles)内的引用,
            for循环并不会创建新的作用域
            // 因此要通过一个闭包的形式拷贝一份img的引用,否则img在for循环结束后将
            只引用最后一次创建的img元素。
            reader.onload = (function(aImg) {
                return function(e) {
                    // e.target.result 包含读取到的 dataURL信息
                    aImg.src = e.target.result
                    // 将图片插入当前文档
                    preview.appendChild(aImg)
                }
            })(img)
            reader.onprogress = function (e) {
                console.log('当前已加载:' + (e.loaded / e.total * 100).toFixed(2)
                        + '%')
            }
            // readAsDataURL方法将file对象读取为dataURL
            reader.readAsDataURL(file)
        }
    }
    var dropzone = document.getElementById('dropzone')
    dropzone.addEventListener('drop', function (e) {
        handleFiles(e.dataTransfer.files)
        e.preventDefault()
        e.stopPropagation()
    }, false)
    dropzone.addEventListener('dragover', function (e) {
        if (e.preventDefault) {
            e.preventDefault()
        }
        return false
    }, false)
</script>
</body>
</html>

用浏览器访问该页面。

从上面的例子可以看到FileReader()的基本用法。readAsDataURL()方法用于读取文件,它接收一个File 或者Blob对象的实例作为参数,并将文件内容转换为一个base64编码的URL字符串,并通过load事件将结果传递到e.target.result上。FileReader对象除了readAsDataURL()方法外,还有其他几个方法用于读取文件内容的操作。

(1) readAsArrayBuffer(Blob|File): 读取文件,最后result属性将包含ArrayBuffer对象以表示文件内容。ArrayBuffer 对象用来表示固定长度二进制数据的缓冲区。
(2) readAsBinaryString(Blob|File):读取文件, result属性包含文件的原始二进制数据。每个字节均由一个[0.255]范围内的整数表示。
(3) readAsText(BloblFile,encoding): 以文本方式读入文件,并可以指定返回数据的编码,默认为UTF-8。
(4) abort): 终止正在进行的读取操作。如果FileReader 对象没有进行读取操作,调用此方法会抛出DOM_FILE_ABORT_ERR异常。

2、Blob 对象

以上读取文件操作的方法有两个共同点,一是都接受一个 Blob或File类型的对象。

var fileParts=['<a>hey man</a>'];
var myB1ob=new B1ob (fileParts,{ "type":"text/xml"});

Blob对象还支持slice() 方法,用于对数据进行切割:

var yourBlob = myB1ob.slice (10,20) ;

File对象同样继承了Blob的slice()方法,可以利用此方法对File对象预先进行分割,然后再读取、上传,最后在服务器端进行组装——异步上传的原理就是这样。如果再记住分割点,这样即使网络中途断掉,也可以在下次传输时从断点续传。

除了都接受Blob和File对象,这些方法另外一个共同点是,由于JavaScript本身基于事件驱动,这些和平台相关的方法都是异步方法。即调用时立即返回,读取文件操作完成后再触发相应的load事件。

除了load事件,FileReader 对象还会调用这样一些事件处理程序。
(1) onabort:当读取操作被终止时调用(调用abort 方法)
(2) onerror: 当读取操作发送错误时调用。
(3) onload: 当读取操作成功完成时调用。
(4) onloadend:当读取操作完成时调用,不管是成功还是失败,该处理程序在onload或者onerror后调用。
(5) onloadstart: 当读取操作将要开始之前调用。
(6) onprogress:在读取数据过程中周期性调用。该事件为最有用的事件,在加载较大的文件时,可以提供一个进度条让用户知道当前加载进度,不让用户产生焦躁感。

reader.onprogress= function(e) {
   console.log('当前文件已加载'+ e.loaded/e. total*100.toFixed(2)+'%') 
}

e.total存储着当前文件的总大小(字节),e.loaded 表示当前文件已经加载了多少。

要想将图片文件转换成可以直接在HTML里引用的URL,除了前文提到的readAsDataURL()方法,还可以使用window.URL .createObjectURL()方法:

var objectURL =window.URL. crea teObjectURL (file0bj) ;

objectURL最后会得到一个类似blob: null/a672ae4c- f84e- 45d2- -87ae-f45dc986d601的字符串,这个字符串可以直接被IMG等元素引用。
objectURL和dataURL一样可以直接被img的src属性引用,就像Windows平台下的文件句柄或者Linux下的文件描述符,在使用完之后通常还要调用window.URL.revokeObjectURL()方法进行释放,如果不显示调用该方法,objectURL 将会在文档卸载时自动释放。对于前文的例子可以简单修改为URL对象版本:

function handleFiles (files) {
   var preview=document.getElementById('preview')
   for(var i=0;i<files.length;i++) {
   var file=files [i]
   ……
   var img=document.createElement("img")
   img.src=window. URL.createObjectURL(file)
   img.onload= function(e) {
      window.URL.revoke0bjectURL(this.src)
   }
   Preview. appendChild (img)
   }
}

有了操作文件的利器,读者可以做一些有趣的功能,比如实现类似Photoshop中图片处理的滤镜或者读取PDF文档并转换为HTML格式等。

发布了13 篇原创文章 · 获赞 2 · 访问量 768

猜你喜欢

转载自blog.csdn.net/sem00000/article/details/105535795