20.4 File API与Blob API

File API与Blob API是为了让Web开发者能以安全的方式访问客户端机器上的文件,从而更好地与这些文件交互而设计的。

20.4.1 File类型

File API仍以表单中的文件输入字段为基础,但增加了直接访问文件信息的能力。
HTML5在DOM上为文件输入元素添加了files集合。
当用户在文件字段中选择一个或多个文件时,这个files集合中会包含一组File对象,表示被选中的文件。
每个File对象都有一些只读属性:
❑ name:本地系统中的文件名。
❑ size:以字节计的文件大小。
❑ type:包含文件MIME类型的字符串。
❑ lastModifiedDate:表示文件最后修改时间的字符串。这个属性只有Chome实现了。

20.4.2 FileReader类型

FileReader类型表示一种异步文件读取机制。
可把FileReader想象成类似于XMLHttpRequest,只不过是用于从文件系统读取文件,而不是从服务器读取数据。
FileReader类型提供了几个读取文件数据的方法:
❑ readAsText(file, encoding):从文件中读取纯文本内容并保存在result属性中。第二个参数表示编码,是可选的。
❑ readAsDataURL(file):读取文件并将内容的数据URI保存在result属性中。
❑ readAsBinaryString(file):读取文件并将每个字符的二进制数据保存在result属性中。
❑ readAsArrayBuffer(file):读取文件并将文件内容以ArrayBuffer形式保存在result属性。这些读取数据的方法为处理文件数据提供了极大的灵活性。
例如,为了向用户显示图片,可以将图片读取为数据URI,而为了解析文件内容,可以将文件读取为文本。
因为这些读取方法是异步的,所以每个FileReader会发布几个事件,其中3个最有用的事件是progress、error和load,分别表示还有更多数据、发生了错误和读取完成。
progress事件每50毫秒就会触发一次,其与XHR的progress事件具有相同的信息:lengthComputable、loaded和total。此外,在progress事件中可以读取FileReader的result属性,即使其中尚未包含全部数据。
error事件会在由于某种原因无法读取文件时触发。触发error事件时,FileReader的error属性会包含错误信息。这个属性是一个对象,只包含一个属性:code。这个错误码的值可能是1(未找到文件)、2(安全错误)、3(读取被中断)、4(文件不可读)或5(编码错误)。
load事件会在文件成功加载后触发。如果error事件被触发,则不会再触发load事件。
如果想提前结束文件读取,则可以在过程中调用abort()方法,从而触发abort事件。

20.4.3 FileReaderSync类型

是FileReader的同步版本。这个类型拥有与FileReader相同的方法,只有在整个文件都加载到内存之后,才会继续执行。
FileReaderSync只在工作线程中可用,因为如果读取整个文件耗时太长则会影响全局。

20.4.4 Blob与部分读取

某些情况下,可能需要读取部分文件而不是整个文件。为此,File对象提供了一个名为slice()的方法。
接收两个参数:起始字节和要读取的字节数。
返回一个Blob的实例,而Blob实际上是File的超类。
blob表示二进制大对象(binary larget object),是JavaScript对不可修改二进制数据的封装类型。
包含字符串的数组、ArrayBuffers、ArrayBufferViews,甚至其他Blob都可以用来创建blob。
Blob构造函数可以接收一个options参数,并在其中指定MIME类型。
Blob对象有size属性和type属性,slice()方法用于进一步切分数据。也可用FileReader从Blob中读取数据。
只读取部分文件可以节省时间,特别是在只需要数据特定部分比如文件头的时候。

20.4.5 对象URL与Blob

对象URL有时候也称作Blob URL,是指引用存储在File或Blob中数据的URL。
对象URL的优点是:不用把文件内容读取到JavaScript也可使用文件。只要在适当位置提供对象URL即可。
要创建对象URL,可用window.URL.createObjectURL()方法,并传入File或Blob对象。
这个函数返回的值是一个指向内存中地址的字符串。
因为这个字符串是URL,所以可以在DOM中直接使用。
使用完数据之后,最好能释放与之关联的内存。
只要对象URL在使用中,就不能释放内存。
如果想表明不再使用某个对象URL,则可以把它传给window.URL.revokeObjectURL()。

20.4.6 读取拖放文件

在页面上创建放置目标后,可从桌面上把文件拖动并放到放置目标。这样会像拖放图片或链接一样触发drop事件。被放置的文件可通过事件的event.dataTransfer.files属性读到,这个属性保存着一组File对象,就像文本输入字段一样。
在drop事件处理程序中,可以通过event.dataTransfer.files读到文件,此时可以获取文件的相关信息。

20.5 媒体元素

大多数内容提供商会强迫使用Flash以便达到最佳的跨浏览器兼容性。
HTML5新增了两个与媒体相关的元素,即

,从而为浏览器提供了嵌入音频和视频的统一解决方案。

  1. <!-- 嵌入视频 -->
  2. <video src="conference.mpg" id="myVideo">视频播放器不可用</video>
  3. <!-- 嵌入音频 -->
  4. <audio src="song.mp3" id="myAudio">音频播放器不可用</audio>

每个元素至少要求有一个src属性,以表示要加载的媒体文件。
可指定表示视频播放器大小的width和height属性,以及在视频加载期间显示图片URI的poster属性。
controls属性如果存在,表示浏览器应该显示播放界面,让用户可以直接控制媒体。
开始和结束标签之间的内容是在媒体播放器不可用时显示的替代内容。
由于浏览器支持的媒体格式不同,因此可以指定多个不同的媒体源。为此,需要从元素中删除src属性,使用一个或多个元素代替

  1. <!-- 嵌入视频 -->
  2. <video id="myVideo">
  3. <source src="conference.webm" type="video/webm" codecs="vp8, vorbis">
  4. <source src="conference.ogv" type="video/ogg" codecs="theora, vorbis">
  5. <source src="conference.mpg">
  6. 视频播放器不可用
  7. </video>
  8. <!-- 嵌入音频 -->
  9. <audio id="myAudio">
  10. <source src="song.ogg" type="audio/ogg">
  11. <source src="song.mp3" type="audio/mp3">
  12. 音频播放器不可用
  13. </audio>

20.5.1 属性

和元素提供了稳健的JavaScript接口。这两个元素有很多共有属性,可以用于确定媒体的当前状态:

20.5.2 事件

除了有很多属性,媒体元素还有很多事件。这些事件会监控由于媒体回放或用户交互导致的不同属性的变化。
image.png

20.5.3 自定义媒体播放器

使用

的play()和pause()方法,可手动控制媒体文件的播放。综合使用属性、事件和这些方法,可以方便地创建自定义的媒体播放器。

  1. <div class="mediaplayer">
  2. <div class="video">
  3. <video id="player" src="movie.mov" poster="mymovie.jpg" width="300" height="200">视频播放器不可用</video>
  4. </div>
  5. <div class="controls">
  6. <input type="button" value="Play" id="video-btn">
  7. <span id="curtime">0</span><span id="duration">0</span>
  8. </div>
  9. </div>

使用JavaScript创建一个简单的视频播放器,上面这个基本的HTML就可被激活

  1. // 取得元素的引用
  2. let player = document.getElementById('player'),
  3. btn = document.getElementById('video-btn'),
  4. curtime = document.getElementById('curtime'),
  5. duration = document.getElementById('duration');
  6. // 更新时长
  7. duration.innerHTML = player.duration;
  8. // 为按钮添加事件处理程序
  9. btn.addEventListener('click', (event) => {
  10. if (player.paused) {
  11. player.play();
  12. btn.value = '暂停';
  13. } else {
  14. player.pause();
  15. btn.value = '播放';
  16. }
  17. });
  18. // 周期性更新当前时间
  19. setInterval(() => {
  20. curtime.innerHTML = player.currentTime;
  21. }, 250);

简单地为按钮添加了事件处理程序,可以根据当前状态播放和暂停视频。此外,还给

元素的load事件添加了事件处理程序,以便显示视频的时长。最后,重复的计时器用于更新当前时间。通过监听更多事件以及使用更多属性,可以进一步扩展这个自定义的视频播放器。同样的代码也可以用于元素以创建自定义的音频播放器。

20.5.4 检测编解码器

并不是所有浏览器都支持

和的所有编解码器,这通常意味着必须提供多个媒体源。为此,也有JavaScript API可以用来检测浏览器,是否支持给定格式和编解码器。这两个媒体元素都有一个名为canPlayType()的方法,该方法接收一个格式/编解码器字符串,返回一个字符串值:”probably”、”maybe”或””(空字符串)。其中空字符串就是假值,意味着可以在if语句中像这样使用canPlayType();“probably”和”maybe”都是真值,在if语句的上下文中可以转型为true。

20.5.5 音频类型

  1. let audio = new Audio("song.mp3");
  2. EventUtil.addHandler(audio, 'canplaythrougn', function(event) {
  3. audio.play();
  4. });

创建Audio的新实例就会开始下载指定的文件。下载完毕后,可以调用play()来播放音频。
在iOS中调用play()方法会弹出一个对话框,请求用户授权播放声音。为了连续播放,必须在onfinish事件处理程序中立即调用play()。

20.6 原生拖放

20.6.1 拖放事件

拖放事件几乎可以让开发者控制拖放操作的方方面面。
关键的部分是确定每个事件是在哪里触发的。
有的事件在被拖放元素上触发,有的事件则在放置目标上触发。
在某个元素被拖动时,会(按顺序)触发以下事件:
(1)dragstart(2)drag(3)dragend
这3个事件的目标都是被拖动的元素。
默认情况下,浏览器在拖动开始后不会改变被拖动元素的外观,因此是否改变外观由你来决定。不过,大多数浏览器此时会创建元素的一个半透明副本,始终跟随在光标下方。
在把元素拖动到一个有效的放置目标上时,会依次触发以下事件:
(1)dragenter(2)dragover(3)dragleave或drop

20.6.2 自定义放置目标

在把某个元素拖动到无效放置目标上时,会看到一个特殊光标(圆环中间一条斜杠)表示不能放下。
即使所有元素都支持放置目标事件,这些元素默认也是不允许放置的。
如果把元素拖动到不允许放置的目标上,无论用户动作是什么都不会触发drop事件。
不过,通过覆盖dragenter和dragover事件的默认行为,可以把任何元素转换为有效的放置目标。

20.6.3 dataTransfer对象

为实现拖动操作中的数据传输,IE5在event对象上暴露了dataTransfer对象,用于从被拖动元素向放置目标传递字符串数据。
因为这个对象是event的属性,所以在拖放事件的事件处理程序外部,无法访问dataTransfer。
在事件处理程序内部,可使用这个对象的属性和方法实现拖放功能。
dataTransfer对象有两个主要方法:getData()和setData()。
getData()用于获取setData()存储的值。
setData()的第一个参数以及getData()的唯一参数是一个字符串,表示要设置的数据类型:”text”或”URL”

  1. // 传递文本
  2. event.dataTransfer.setData('text', '一些文本');
  3. let text = event.dataTransfer.getData('text');
  4. // 传递URL
  5. event.dataTransfer.setData('URL', 'http://www.wrox.com');
  6. let url = event.dataTransfer.getData('URL');

为向后兼容,HTML5还会继续支持”text”和”URL”,但它们会分别被映射到”text/plain”和”text/uri-list”。
dataTransfer对象实际上可以包含每种MIME类型的一个值,也就是说可以同时保存文本和URL,两者不会相互覆盖。存储在dataTransfer对象中的数据只能在放置事件中读取。
作为文本的数据和作为URL的数据有一个区别。当把数据作为文本存储时,数据不会被特殊对待。而当把数据作为URL存储时,数据会被作为网页中的一个链接,意味着如果把它放到另一个浏览器窗口,浏览器会导航到该URL。

20.6.4 dropEffect与effectAllowed

dataTransfer对象不仅可以用于实现简单的数据传输,还可以用于确定能够对被拖动元素和放置目标执行什么操作。
为此,可用两个属性:dropEffect与effectAllowed。
dropEffect属性可以告诉浏览器允许哪种放置行为。
这个属性有以下4种可能的值:
❑ “none”:被拖动元素不能放到这里。这是除文本框之外所有元素的默认值。
❑ “move”:被拖动元素应该移动到放置目标。
❑ “copy”:被拖动元素应该复制到放置目标。
❑ “link”:表示放置目标会导航到被拖动元素(仅在它是URL的情况下)。
除非同时设置effectAllowed,否则dropEffect属性也没有用。
effectAllowed属性表示对被拖动元素是否允许dropEffect。
假设我们想允许用户把文本从一个文本框拖动到一个

元素。那么必须同时把dropEffect和effectAllowed属性设置为”move”。

20.6.5 可拖动能力

默认情况下,图片、链接和文本可拖动,这意味着无须额外代码用户便可以拖动它们。
文本只有在被选中后才可以拖动,而图片和链接在任意时候都是可以拖动的。
我们也可以让其他元素变得可以拖动。
HTML5在所有HTML元素上规定了一个draggable属性,表示元素是否可以拖动。
图片和链接的draggable属性自动被设置为true,而其他所有元素此属性的默认值为false。
如果想让其他元素可拖动,或者不允许图片和链接被拖动,都可以设置这个属性。

20.6.6 其他成员

HTML5规范还为dataTransfer对象定义了下列方法:
❑ addElement(element):为拖动操作添加元素。这纯粹是为了传输数据,不会影响拖动操作的外观。在本书写作时,还没有浏览器实现这个方法。
❑ clearData(format):清除以特定格式存储的数据。
❑ setDragImage(element, x, y):允许指定拖动发生时显示在光标下面的图片。这个方法接收3个参数:要显示的HTML元素及标识光标位置的图片上的x和y坐标。这里的HTML元素可以是一张图片,此时显示图片;也可以是其他任何元素,此时显示渲染后的元素。
❑ types:当前存储的数据类型列表。这个集合类似数组,以字符串形式保存数据类型,比如”text”。