html5 提供了一些关于文件操作的 API,其中包括用于读取文件内容的 FileReader API,以及可以存取受浏览器沙箱保护的文件系统的 FileSystem API。

这节将主要介绍这两个 API。同时还会介绍代表原始二进制数据的 Blob 对象,代表一个用于存放原始二进制数据的缓存区的 ArrayBuffer 对象,以及可向缓存区中写入或从缓存区中读出数据的 ArrayBufferView 对象与 DataView 对象。

FileList对象与file对象

FileList 对象表示用户选择的文件列表,html4 中 input[type=”fiile”] 只允许放置一个文件,而在 html5 中,通过添加 multiple 属性,可以允许放置多个文件。

在 file 控件内,用户选择的每一个文件都是一个 file 对象,当用户选择多个文件时,使用 fileList 对象列表存储每一个 file 对象。
**
file 对象的属性:

  • name 属性表示文件名,不包括路径;
  • lastModifiedDate 属性表示文件的最后修改日期(时间戳);
  • size:表示文件大小(字节);
  • type:表示文件类型;
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <input type="file" id="file" multiple size="80">
  11. <input type="button" value="文件上传" onclick="showFile();">
  12. </body>
  13. </html>
  14. <script>
  15. function showFile() {
  16. var file;
  17. var fileList = document.getElementById('file').files; // fileList文件列表对象
  18. for (let i = 0; i < fileList.length; i++) {
  19. file = fileList[i]; // 用户选择的单个文件对象file
  20. console.log(file.name);
  21. }
  22. }
  23. </script>

ArrayBuffer对象与ArrayBufferView对象

HTML 5 为更高效的访问原始二进制数据新增了两种对象:ArrayBuffer对象ArrayBufferView对象。

目前,Chrome 9以上、Firefox 17以上、IE 10以上、Opera 12以上、Safari 6以上版本的浏览器均支持 ArrayBuffer 对象与 ArrayBufferView 对象。

ArrayBuffer对象

ArrayBuffer 对象代表一个存储固定长度的二进制数据的缓存区不能直接存取 ArrayBuffer 缓存区中的内容,必须通过 ArrayBufferView 对象来读写 ArrayBuffer 缓存区中的内容。

html5 中通过如下方式创建一个 ArrayBuffer 对象。参数必须是一个无符号长整数类型的参数,用于指定缓存区的长度,单位是 byte。在成功创建 ArrayBuffer 缓存区之后,该缓存区中的所有数据都被初始化为 0。

ArrayBuffer 对象具有一个 byteLength 属性,属性值为缓存区长度。

  1. <!-- Firefox 4以上、Opera 11.6以上、Chrome 7以上、Safari 5.1以上、IE 10版本的浏览器支持 -->
  2. var buf = new ArrayBuffer(5);

image.png

ArrayBufferView对象

在 HTML5 中,通过 ArrayBufferView 对象以一种准确的格式来表示 ArrayBuffer 缓存区中的数据。

html5 中,一般不直接使用 ArrayBufferView 对象,而是使用继承 ArrayBufferView 类的子类的实例对象来存取 ArrayBuffer 缓存区中的数据。继承 ArrayBufferView 类的各种子类如下表所示。

文件API - 图2

在上表中,Uint8ClampedArray 类型用于定义一种特殊的 8 位无符号整数数组,该数组的作用是代替CanvasPixelArray 数组用于Canvas API中(CanvasPixelArray 数组的用法将在后续章节中进行描述)。该数组与普通 8 位无符号整数数组的区别是在转换 ArrayBuffer 缓存区中的数值时,内部使用箝位(clamping)算法,而不是使用模数(modulo)算法。

ArrayBufferView 对象根据同一个 ArrayBuffer 对象来创建各种数值类型的数组。示例如下:

  1. var buf = new ArrayBuffer(5);
  2. // 根据ArrayBuffer对象创建8位整数数组
  3. // var int8Array = new Int8Array(buf);
  4. // 根据同一个arrayBuffer对象创建8位无符号整数数组
  5. var uInt8Array = new Uint8Array(buf);

image.png

在创建一个 ArrayBufferView 对象时,除了需要指定该对象所要引用的 ArrayBuffer 缓存区外,可以使用如下两个参数:

  • byteOffset:一个无符号长整型数值,代表数组所开始引用的位置与 ArrayBuffer 缓存区的第一个字节之间的偏离值,单位为字节,属性值必须为数组中单个元素的字节长度的倍数,不指定该参数值时 ArrayBufferView 对象从 ArrayBuffer 缓存区的第一个字节开始引用;
  • byteLength:一个无符号长整型数值,代表数组中元素的个数,不指定该参数值时脚本程序将根据缓存区长度(以字节为单位)、ArrayBufferView 对象所开始引用的位置与每个元素的字节长度自动计算出元素个数。

ArrayBufferView 具有如下所示的三个属性:

  • buffer:只读属性,属性值为 ArrayBuffer 对象,代表 ArrayBufferView 对象所引用的 ArrayBuffer 缓存区;
  • byteOffset:只读属性,属性值为一个无符号长整型数值,代表 ArrayBufferView 对象所开始引用的位置与 ArrayBuffer 缓存区的第一个字节之间的偏离值,单位为字节;
  • length:只读属性,属性值为一个无符号长整型数值,代表数组中元素的个数。

使用 ArrayBufferView 时,使用如下所示的方法来存取 ArrayBuffer 缓存区中的数据。

  1. var byte = uInt8Array[4]; // 读取第5个字节的数据
  2. uInt8Array[4] = 1; // 设置第5个字节的数据

image.png

DataView对象

除了上图中所示的继承 ArrayBufferView 的子类对象之外,html5 也提供了继承 ArrayBufferView 类的 DataView 类的实例对象来存取 ArrayBuffer 缓存区中的数据。DataView 对象提供了存取 ArrayBuffer 缓存区中数据的一些方法。

创建 DataView 对象的方法如下:

  1. // 创建 DataView 对象
  2. let dataview = new DataView(buffer, byteOffset, byteLength);

DataView 构造函数中提供了三个参数:

  • buffer:一个 ArrayBuffer 对象,代表一个 ArrayBuffer 缓存区;
  • byteOffset:一个无符号长整型数值,代表 DataView 对象所开始引用的位置与 ArrayBuffer 缓存区的第一个字节之间的偏离值,单位为字节,不指定该参数值时 DataView 对象从 ArrayBuffer 缓存区的第一个字节开始引用;
  • byteLength:一个无符号长整型数值,代表 DataView 对象的总字节长度。

DataView 对象可以使用的方法如下所示:

  • getInt8(byteOffset):用于得到指定位置处的一个8位整数;
  • setInt8(byteOffset,value):用于设定指定位置处的一个8位整数的数值;
  • getUint8(byteOffset):用于得到指定位置处的一个8位无符号型整数;
  • setUint8(byteOffset,value):用于设定指定位置处的一个8位无符号型整数的数值;
  • getInt16(byteOffset):用于得到指定位置处的一个16位整数;
  • setInt16(byteOffset,value):用于设定指定位置处的一个16位整数的数值;
  • getUint16(byteOffset):用于得到指定位置处的一个16位无符号型整数;
  • setUint16(byteOffset,value):用于设定指定位置处的一个16位无符号型整数的数值;
  • getInt32(byteOffset):用于得到指定位置处的一个32位整数;
  • setInt32(byteOffset,value):用于设定指定位置处的一个32位整数的数值;
  • getUint32(byteOffset):用于得到指定位置处的一个32位无符号型整数;
  • setUint32(byteOffset,value):用于设定指定位置处的一个32位无符号型整数的数值;
  • getFloat32(byteOffset):用于得到指定位置处的一个32位浮点数;
  • setFloat32(byteOffset,value):用于设定指定位置处的一个32位浮点数的数值;
  • getFloat64(byteOffset):用于得到指定位置处的一个64位浮点数;
  • setFloat64(byteOffset,value):用于设定指定位置处的一个64位浮点数的数值;

下面是一个对于 getUnit8 和 setUint8 的一个例子,其他方法类似:

  1. <script>
  2. // 设置一个固定长度的ArrayBuffer缓存区,指定长度的5个byte,初始数据都为0
  3. var buf = new ArrayBuffer(5);
  4. // console.log(buf);
  5. // 因为不能直接存取ArrayBuffer缓存区的数据,所以需要ArrayBufferView对象来读写缓存区的内容
  6. // html5中一般不直接使用ArrayBufferView类,而是使用继承自ArrayBufferView类的子类来实例化对象
  7. // 这里使用的是Uint8Array子类来实例化对象,通过这个实例对象来存取缓存区中的数据
  8. var uInt8Array = new Uint8Array(buf);
  9. // 设置第3个字节的数据
  10. uInt8Array[3] = 3;
  11. // console.log(uInt8Array);
  12. // 除了上面的子类,也可以通过DataView子类来实例化对象来存取缓存区中的数据
  13. // DataView对象提供了直接存取ArrayBuffer缓存区的数据的方法
  14. // 因为继承自ArrayBufferView,DataView构造函数同样提供了三个参数,具体看直接的描述
  15. // 这里我们尝试使用了偏离值byteOffset,偏离值是1,即从第二个数组位置开始引用
  16. var view = new DataView(buf, 1);
  17. console.log(view);
  18. // 使用DataView提供的方法getUint8来获取缓存区的数据,由于上一步初始化的时候设置了从第二个数组位置开始引用
  19. // 加上这里传入的偏离值是2,即对于整个缓存区是第四个位置的值(即下标为3的值)
  20. // 而前面我们设置了下标为3的值是3,所以这里输出的值也是3
  21. var int8Value = view.getUint8(2);
  22. console.log(int8Value);
  23. // 同理使用setUint8来设置缓存区的数据,从第二个数组位置开始引用,加上偏离值3,
  24. // 即设置下标为4的值为4
  25. // 继续getUint8输出结果
  26. view.setUint8(3, 4);
  27. var int8Value = view.getUint8(3);
  28. console.log(int8Value);
  29. </script>

最终结果如下图所示。
image.png

Blob对象

Blob对象概述

在 HTML5 中,新增一个 Blob 对象,代表原始二进制数据。事实上,上面提到的 file 对象也继承了这个 Blob 对象。

Blob 对象有两个属性,size 属性表示一个 Blob 对象的字节长度,type 属性表示 Blob 的 MIME 类型,如果是未知类型,则返回一个空字符串。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <input type="file" id="file">
  11. <input type="button" value="显示文件信息" onclick="showFileType();">
  12. <p>
  13. 文件字节长度:<span id="size"></span><br>
  14. 文件类型:<span id="type"></span><br>
  15. </p>
  16. </body>
  17. </html>
  18. <script>
  19. function showFileType() {
  20. var file;
  21. // 得到用户选择的第一个文件
  22. file = document.getElementById('file').files[0];
  23. var size = document.getElementById('size');
  24. // 显示文件字节长度
  25. size.innerHTML=file.size;
  26. var type = document.getElementById('type');
  27. type.innerHTML=file.type;
  28. }
  29. </script>

上传一张图片测试,结果如下所示。
image.png

Blob 对象的 type 属性都是以“image/”开头的,后跟图像类型,利用此特性我们可以在 JavaScript 中判断用户选择的文件是否为图像文件。

另外,HTML5 中已经对 file 控件添加了 accept 属性,意图让 file 控件只能接受某种类型的文件,但是目前各主流浏览器对它的支持都只限于打开文件选择窗口时,默认选择图像文件而已,如果选择其他类型文件,file 控件也能正常接受。

  1. <input type="file" id="file" accept="image/*">

image.png

创建Blob对象

到目前为止,各主流浏览器的最新版均已支持 Blob 对象的创建及使用。

在 HTML5 中,可以直接创建一个代表原始二进制数据的 Blob 对象,示例如下。

  1. var blob = new Blob([blobParts, type]);

它接收两个参数,两个参数都是可选的。第一个参数值为一个数组,其中可以存放任意数量个以下类型的对象,这些对象中所携带的数据将被依序追加到 Blob 对象中。

  • ArrayBuffer 对象
  • ArrayBufferView 对象
  • Blob 对象
  • String 对象

第二个参数值用于指定被创建的 Blob 对象的 type 属性值(代表 Blob 对象的 MIME 类型),当不使用该参数值时,默认参数值为空字符串,代表未知类型。

创建示例如下所示。

  1. // 使用第一个参数值创建
  2. var buffer = new ArrayBuffer(1000);
  3. var blob = new Blob(["1234"+"5678"]);
  4. var shorts = new Uint16Array(buffer, 512, 128);
  5. var blobA = new Blob([blob, shorts]);
  6. var bytes = new Uint8Array(buffer, shorts.byteOffset+shorts.byteLength);
  7. var blobB = new Blob([blob, blobA, bytes]);
  8. var blobC = new Blob([buffer, blob, blobA, bytes]);
  9. console.log(blobC);
  10. // 使用第二个参数值创建
  11. var blob = new Blob(["1234"+"5678"], {type: "text/plain"});
  12. var blob = new Blob(["1234"+"5678"], {type: "text/plain;charset=UTF-8"});
  13. console.log(blob)

image.png
image.png

在 HTML5 中,可以利用浏览器窗口对象的 URL 属性值(window.URL)或 webkitURL 属性值(chrome浏览器)获取浏览器窗口的 URL 对象,通过该对象的 createObjectURL 方法可以根据一个 Blob 对象的二进制数据来创建一个 URL 地址并返回该地址,当用户访问该 URL 地址时可以直接下载原始二进制数据。

下面是一个例子。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <textarea id="text" placeholder="请输入一些文字"></textarea><br>
  11. <button onclick="blob_test()">创建及下载二进制文件</button>
  12. <output id="result"></output>
  13. </body>
  14. </html>
  15. <script>
  16. // 创建及下载二进制文件
  17. function blob_test() {
  18. var text = document.getElementById('text').value;
  19. var blob;
  20. var result = document.getElementById('result');
  21. // 创建Blob对象
  22. if(!window.Blob) {
  23. result.innerHTML="您的浏览器不支持使用Blob对象";
  24. } else {
  25. blob = new Blob([text]); // Blob中数据为文字时默认使用utf8格式
  26. // 通过createObjectURL方法创建文字链接
  27. if(window.URL) {
  28. result.innerHTML='<a download href="'+window.URL.createObjectURL(blob)+'" target="_blank">文件下载</a>';
  29. }
  30. }
  31. }
  32. </script>

在 textarea 元素中输入“这是一段测试内容”,单击“创建及下载二进制文件”按钮,脚本程序根据用户输入文字创建二进制对象,然后根据该二进制对象中的内容创建 URL 地址,然后在“创建及下载二进制文件”按钮旁动态添加一个“文件下载”链接。点击“文件下载”下载文件,打开该文件内容即为输入的内容。如下图所示。
image.png

Blob对象的slice方法

Blob 对象具有一个 slice 方法,用于从 Blob 对象所代表的原始二进制数据中抽离一部分数据,然后将这些数据创建为一个新的 Blob 对象。

  1. var newBlob = blob.slice([start, end, contentType]);

Blob 对象的 slice 方法使用三个参数,均为可选参数,如果三个参数均省略时,相当于把一个 Blob 对象中的原始二进制数据原样复制到一个新建的 Blob 对象(即 slice 方法的返回值)中。

start 和 end 分别为复制的起始位置和结束位置,contentType 指定新建 Blob 对象中数据的 MIME 类型。
新建的 Blob 对象的 size 属性值为 start 到 end 范围内的长度,单位为 byte。

示例如下。

  1. var file = document.getElementById('file').files[0];
  2. var newFile = file.slice(0, file.size, "image/png");

FileReader对象

FileReader 对象主要用来把文件读入内存,并且读取文件中的数据。

Firefox 3.6以上、Chrome 6以上、Safari 5.2以上、Opera 11以上和IE 10版本的浏览器支持,可以使用下面方法检测浏览器是否支持。

  1. if(typeof FileReader === 'undefined') {
  2. alert('您的浏览器未实现FileReader接口');
  3. } else {
  4. var reader = new FileReader();
  5. // ...
  6. }

FileReader对象的方法

FileReader 对象拥有5个方法,其中4个用以读取文件,另一个用来中断读取过程。fileReader 对象读取到的数据都保存在 result 属性中。

文件API - 图11

  • readAsBinaryString(Blob):将 Blob 对象或者文件数据读取为二进制字符串;
  • readAsText(Blob [, encoding]):将 Blob 对象或者文件数据以文本方式读取;第二个参数指定文本的编码方式,默认是UTF-8;
  • readAsDataURL(Blob):将 Blob 对象或文件中的数据读取为一串 Data URL 字符串。事实上是将数据以一种特殊 的 URL 形式直接读入页面;
  • readAsArrayBuffer(Blob):将 Blob 对象或文件中的数据读取为一个 ArrayBuffer 对象;

FileReader对象的事件

FileReader 对象拥有一套完整的事件模型,用于捕获读取文件时的状态。事件如下表。

文件API - 图12

FileReader对象的方法使用示例

下面针对 fileReader 对象的 readAsBinaryString 方法、readAsText 方法、readAsDataURL 方法和 readAsArrayBuffer 方法进行讲解。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <p>
  11. <label>请选择一个文件;</label>
  12. <input type="file" id="file" />
  13. <input type="button" value="读取图像" onclick="readAsDataURL()" />
  14. <input type="button" value="读取二进制数据" onclick="readAsBinaryString()" />
  15. <input type="button" value="读取文本文件" onclick="readAsText()" />
  16. <input type="button" value="以ArrayBuffer方式读取" onclick="readAsArrayBuffer()" />
  17. </p>
  18. <div name="result" id="result"></div>
  19. </body>
  20. </html>
  21. <script>
  22. var result=document.getElementById('result');
  23. var file=document.getElementById('file');
  24. if(typeof FileReader == 'undefined') {
  25. result.innerHTML="<p>您的浏览器不支持FileReader</p>";
  26. file.setAttribute('disabled', 'disabled');
  27. }
  28. // 将文件以Data URL形式读入页面
  29. function readAsDataURL() {
  30. // 检查是否为图像
  31. var file = document.getElementById('file').files[0];
  32. if(!/image\/\w+/.test(file.type)) {
  33. alert("请确保文件为图像类型");
  34. return false;
  35. }
  36. var reader = new FileReader();
  37. reader.readAsDataURL(file);
  38. reader.onload = function(e) {
  39. // fileReader 对象读取到的数据都保存在 result 属性中
  40. result.innerHTML='<img src="'+this.result+'" alt=""/>';
  41. }
  42. }
  43. // 将文件以二进制形式读入页面
  44. function readAsBinaryString() {
  45. var file = document.getElementById('file').files[0];
  46. var reader = new FileReader();
  47. reader.readAsBinaryString(file);
  48. reader.onload= function(e) {
  49. result.innerHTML=this.result;
  50. }
  51. }
  52. // 将文件以文本文件形式读入页面
  53. function readAsText() {
  54. var file = document.getElementById('file').files[0];
  55. var reader = new FileReader();
  56. reader.readAsText(file, 'utf-8');
  57. reader.onload = function(e) {
  58. result.innerHTML = this.result;
  59. }
  60. }
  61. // FileReader对象的readAsArrayBuffer方法示例
  62. function readAsArrayBuffer() {
  63. var file = document.getElementById('file').files[0];
  64. // 使用file对象的slice方法将文件中前4个字节的内容复制到一个Blob对象中
  65. // var blob = file.mozSlice(0,4);
  66. var blob = file.slice(0,4);
  67. // 通过FileReader对象的readAsArrayBuffer方法将blob数据读取为一个ArrayBuffer对象
  68. var reader = new FileReader();
  69. reader.readAsArrayBuffer(blob);
  70. // 使用DataView对象读取ArrayBuffer缓存区中位于开头位置的一个32位整数
  71. reader.onload = function(e) {
  72. var buffer = this.result;
  73. var view = new DataView(buffer);
  74. var magic = view.getInt32(0, false);
  75. var type;
  76. // 根据整数值判断用户选取文件的文件类型,并显示在页面
  77. if(magic<0) {
  78. magic = magic + 0x100000000;
  79. magic=magic.toString(16).toUpperCase();
  80. console.log(magic);
  81. if(magic.indexOf('FFD8FF') >=0) type = "jpg文件";
  82. if(magic.indexOf('89504E47') >=0) type = "png文件";
  83. if(magic.indexOf('47494638') >=0) type = "gif文件";
  84. if(magic.indexOf('49492A00') >=0) type = "tif文件";
  85. if(magic.indexOf('424D') >=0) type = "bmp文件";
  86. document.getElementById('result').innerHTML="选择的文件类型是: "+type;
  87. }
  88. }
  89. }
  90. </script>

选取一张图片,点击“读取图片”按钮,效果如下:
image.png

同样选择上图,点击“读取二进制数据”按钮,效果如下:
image.pngimage.png

创建一个测试文件,往其中添加一些内容,然后选择这个测试文件,点击“读取文本文件”,可以将测试文件中的内容读取出来:
image.png

选择一个 png 文件,点击以 ArrayBuffer 方式读取,最终显示的 png 文件。
image.png

FileReader对象读取文件的状态示例

前面说到,当 fileReader 对象读取文件时,伴随着一系列事件,用于表示读取文件时不同的读取状态。下面测试一下这些状态的先后顺序。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <p>
  11. <label>请选择一个图像文件:</label>
  12. <input type="file" id="file">
  13. <input type="button" value="显示图像" onclick="readFile()">
  14. <div name="result" id="result"></div>
  15. </p>
  16. </body>
  17. </html>
  18. <script>
  19. var result = document.getElementById('result');
  20. var input = document.getElementById('input');
  21. if(typeof FileReader == 'undefined') {
  22. result.innerHTML="<p>抱歉,您的浏览器不支持FileReader</p>";
  23. result.setAttribute('disabled', 'disabled');
  24. }
  25. function readFile() {
  26. var file = document.getElementById('file').files[0];
  27. var reader = new FileReader();
  28. reader.onloadstart=function() {
  29. console.log('数据读取开始时触发:laodstart');
  30. }
  31. reader.onprogress= function() {
  32. console.log('数据读取中:progress');
  33. }
  34. reader.onabort= function() {
  35. console.log('数据读取中断时触发:onabort');
  36. }
  37. reader.onload = function(e) {
  38. result.innerHTML='<img src="'+this.result+'" />';
  39. console.log('数据读取完成时触发:load');
  40. }
  41. reader.onerror= function() {
  42. console.log('数据读取失败时触发:error');
  43. }
  44. reader.onloadend=function(){
  45. console.log('数据读取完成时,无论成功失败都触发:loadend')
  46. }
  47. reader.readAsDataURL(file);
  48. }
  49. </script>

选择一张图片,点击“显示图像”,输出读取状态。
image.png

to be continue…