H5 FILE API

simplify the life

HTML5 File API — 让前端操作文件变的可能

前言

在 HTML5 File API 出现之前,前端对于文件的操作是非常有局限性的,大多需要配合后端实现。出于安全角度考虑,从本地上传文件时,代码不可能获取文件在用户本地的地址,所以纯前端不可能完成一些类似图片预览的功能。但是 File API 的出现,让这一切变成了可能。

跟着楼主由浅入深,了解下强大的 File API 吧。

FileList

FileList 对象针对表单的 file 控件。当用户通过 file 控件选取文件后,这个控件的 files 属性值就是 FileList 对象。它在结构上类似于数组,包含用户选取的多个文件。如果 file 控件没有设置 multiple 属性,那么用户只能选择一个文件,FileList 对象也就只有一个元素了。

复制代码

比如我选择了两个文件,控制台打印:

复制代码FileList {0:File,1:File, length:2}0:File1:Filelength:2__proto__: Object

除了用 file 控件,采用拖放方式,也可以得到 FileList 对象。关于拖放,可以参考下我以前写的文章HTML5 — 让拖放变的流行起来

复制代码

选中两个文件拖放到文本框中,打印结果和上面一致。

一般来说,我们不可能手动构造 FileList 对象,只能被动地读取,也就是说只有用户主动触发了文件读取行为,js 才能访问到 FileList,而这通常发生在表单选择文件或者拖拽文件中。

File

我们看到一个 FileList 对象包含了我们选中的 File 对象,那么一个 File 又有哪些属性呢?我们可以打印出来看看。

name:文件名,该属性只读。

size:文件大小,单位为字节,该属性只读。

type:文件的 MIME 类型,如果分辨不出类型,则为空字符串,该属性只读。

lastModified:文件的上次修改时间,格式为时间戳。

lastModifiedDate:文件的上次修改时间,格式为 Date 对象实例。

Blob

上图中我们看到,File 对象是继承自 Blob 对象的,Blob 又是什么鬼?

Blob(Binary Large Object)对象代表了一段二进制数据,提供了一系列操作接口。其他操作二进制数据的 API(比如 File 对象),都是建立在 Blob 对象基础上的,继承了它的属性和方法。

生成 Blob 对象有两种方法:一种是使用 Blob 构造函数,另一种是对现有的 Blob 对象使用 slice 方法切出一部分。

(1)Blob 构造函数,接受两个参数。第一个参数是一个包含实际数据的数组,第二个参数是数据的类型,这两个参数都不是必需的。

复制代码vara=["hello","world"];varmyBlob=newBlob(a,{"type":"text/xml"});console.log(myBlob);

(2)Blob 对象的 slice 方法,将二进制数据按照字节分块,返回一个新的 Blob 对象。

复制代码vara=["hello","world"];varmyBlob=newBlob(a,{"type":"text/xml"});varnewBlob=myBlob.slice(0,5);console.log(newBlob);

Blob 对象有两个只读属性:

size:二进制数据的大小,单位为字节。(文件上传时可以在前端判断文件大小是否合适)

type:二进制数据的 MIME 类型,全部为小写,如果类型未知,则该值为空字符串。(文件上传时可以在前端判断文件类型是否合适)

FileReader

重头戏来了,FileReader API 才是我们接下去完成一些任务的关键。FileReader API 用于读取文件,即把文件内容读入内存。它的参数是 File 对象或 Blob 对象

对于不同类型的文件,FileReader 提供不同的方法读取文件。

readAsBinaryString(Blob|File):返回二进制字符串,该字符串每个字节包含一个 0 到 255 之间的整数。(已废弃)

readAsText(Blob|File, opt_encoding):返回文本字符串。默认情况下,文本编码格式是 UTF-8,可以通过可选的格式参数,指定其他编码格式的文本。

readAsDataURL(Blob|File):返回一个基于 Base64 编码的 data-uri 对象。

readAsArrayBuffer(Blob|File):返回一个 ArrayBuffer 对象。

除了以上四种不同的读取文件方法,FileReader API 还有一个 abort 方法,用于中止文件上传。

复制代码varreader=newFileReader();reader.abort();

FileReader 对象采用异步方式读取文件,可以为一系列事件指定回调函数。

onabort 方法:读取中断或调用 reader.abort() 方法时触发。

onerror 方法:读取出错时触发。

onload 方法:读取成功后触发。

onloadend 方法:读取完成后触发,不管是否成功。触发顺序排在 onload 或 onerror 后面。

onloadstart 方法:读取将要开始时触发。

onprogress 方法:读取过程中周期性触发。(可以用来获取文件读取的进度)

以前在学习图片的 base64 编码的时候,写了一篇文章获取图片 base64 编码的几种方法,当时还没有学习 File API,了解到 File API 也能做类似的事情,现在学到了,写了个简单的 demohttp://hanzichi.github.io/2016/image2base64/,不仅能获取图片的 base64 编码,同时也能获取文字的 base64 编码,代码比较简单就不放了,可以猛戳这里

获取到了文件的 base64 编码,做一些诸如图片预览的功能,也就手到擒来了,有兴趣的可以自己尝试下,类似的还有文字预览啊,等等。

URL

你以为 File API 就这样了吗?非也,还有个强大的东西没有介绍,URL 对象!

调用 URL 对象的 createObjectURL 方法,传入一个 File 对象或者 Blob 对象,能生成一个链接,听起来好像很吊的样子。

复制代码varobjecturl=window.URL.createObjectURL(blob);

上面的代码会对二进制数据生成一个 URL,这个 URL 可以放置于任何通常可以放置 URL 的地方,比如 img 标签的 src 属性。需要注意的是,即使是同样的二进制数据,每调用一次 URL.createObjectURL 方法,就会得到一个不一样的 URL。

这个 URL 的存在时间,等同于网页的存在时间,一旦网页刷新或卸载,这个 URL 就失效。(File 和 Blob 又何尝不是这样呢)除此之外,也可以手动调用 URL.revokeObjectURL 方法,使 URL 失效。

复制代码window.URL.revokeObjectURL(objectURL);

举个简单的例子。

复制代码varblob=newBlob(["Hello hanzichi"]);vara=document.createElement("a");a.href=window.URL.createObjectURL(blob);a.download="a.txt";a.textContent="Download";document.body.appendChild(a);

页面上生成了一个超链接,点击它就能下载一个名为a.txt的文件,里面的内容是Hello hanzichi。

这里插点题外话,简单介绍下 H5 新增的 download 属性。对于一些诸如 exe,rar 等浏览器不能直接打开的文件类型,我们一般可以直接用一个 a 标签,将其指向文件在服务端的地址,点击即可下载。但是如果是一些浏览器能直接打开的文件,比如 txt,js 等,如果这样设置一个超链接,点击会直接打开文件,一般我们可以配合后端实现,比如用 PHP。

复制代码$file_name="1.txt";// 下载文件名$file_dir=dirname(__FILE__).'/';//下载文件存放目录//输入文件标签Header("Content-type: text/plain");Header("Content-Disposition: attachment; filename=".$file_name);

以上代码需要文件的 Content-type 属性值,安利一个网址,http://tool.oschina.net/commons,各种文件类型的 Content-type 属性值一网打尽!

如果考虑到安全性,header + fread 可能会显得更严谨。

复制代码$file_name="1.txt";// 下载文件名$file_dir=dirname(__FILE__).'/';//下载文件存放目录Header("Content-type: text/plain");Header("Content-Disposition: attachment; filename=".$file_name);echofread($file,filesize($file_dir.$file_name));

但是现在我们只需要在 a 标签上加上 download !

复制代码download txt>

还可以给 download 加上属性值,即为下载的文件名。

复制代码download txt>

可以省略.txt的后缀名,浏览器会自行判断。

我们再回到 URL 上来。对于 File 或者 Blob 对象,我们可以这样理解,它们的存在,依赖于页面,而 URL 能给这些 "转瞬即逝" 的二进制对象一个临时的指向地址。

这个临时的地址还有什么用呢?也能做图片预览,相比前面用 readAsDataURL 的实现,更简单了。

复制代码

need-to-insert-img

比如还有这样的需求,前端上传文件,要动态生成该文件的下载链接,也能用 URL 完成。

Canvas & dataURL & Blob

canvas 中有 toDataURL 函数,可以将 canvas 转为 dataURL 形式的 base64 编码,而 Blob 也可以转为 dataURL,这三者之间是否可以互相转换?有没有什么实用之处?

(1)canvas -> dataURL

用 toDataURL 方法,比较简单,不多说。

(2)blob -> dataURL

用 FileReader 的 readAsDataURL 方法,使用方式可以看这个 demo

(3)dataURL -> blob

这个函数有点屌

复制代码functiondataURLtoBlob(dataurl){vararr=dataurl.split(','),mime=arr[0].match(/:(.*?);/)[1],bstr=atob(arr[1]),n=bstr.length,u8arr=newUint8Array(n);while(n--){u8arr[n]=bstr.charCodeAt(n);}returnnewBlob([u8arr],{type:mime});}

(4)dataURL - canvas

将 image 的 src 属性置为 dataURL,再用 drawImage 方法画上去。

(5)blob - canvas

如何把二进制形式的图片画上 canvas?先用 readAsDataURL 转为 dataURL,接着就是 (4) 的事情了。

(6)canvas - blob

canvas 转为 blob 也可以用 dataURL 做跳板,先将 canvas 转为 dataURL(1),再用 dataURL 转为 blob(3)。

利用它们之间的转换可以做些什么好玩的事呢?比如可以上传图片,对图片做各种处理,然后保存,看起来好像挺好玩的,等有空了搞个 demo 出来。

2016.11.11 add:canvas 有原生的toBlob方法,使得图片文件可以被缓存或保存到本地。

Read more

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容