浏览器通过FileReader 对象从文件中读取数据比较好理解。在上篇文章中, 你学到如何轻松的使用 FileReader 的各种格式从文件中读取数据。FileReader 在很多方面与 XMLHttpRequest 非常相似。

进度事件

进度事件虽如此普遍事实上它们是在另外的规范中提出的[1]。 这些事件是专门为展示数据的传输过程而设计的。比如使用FileReader 从服务器或者硬盘中读数据的过程。

六个进度事件如下:

  • loadstart – 表明进度开始。这是第一个触发的事件。
  • progress – 数据加载过程中不停觖发.
  • error – 数据加载失败触发.
  • abort –当调用 abort()来取消数据加载时触发 (XMLHttpRequestFileReader通用).
  • load – 全部数据成功加载完毕触发。
  • loadend – 加载结束时触发,无论是errorabort, 或者 load都触发。

上篇文章中已经提到过error 和 load,其他事件将让你能够更细粒度的控制数据传输。

进度追踪

progress 事件可以追踪进度。此事件携带三个数据传输过程的属性值:

  • lengthComputable – 布尔值,表明当前浏览器能否传输完整的数据。
  • loaded – 已经读取的字节数。
  • total – 要读取的字节总数。

这些值可以用于进度条显示的进度信息。例如,要以用一个HTML5的<progress> 元素即时显示读取一个文件的进度值。配合进度值你可以这么使用:

var reader = new FileReader(),
     progressNode = document.getElementById("my-progress");

reader.onprogress = function(event) {
    if (event.lengthComputable) {
        progressNode.max = event.total;
        progressNode.value = event.loaded;
    }
};

reader.onloadend = function(event) {
    var contents = event.target.result,
        error    = event.target.error;

    if (error != null) {
        console.error("File could not be read! Code " + error.code);
    } else {
        progressNode.max = 1;
        progressNode.value = 1;
        console.log("Contents: " + contents);
    }
};

reader.readAsText(file);

这有点像Gmail的拖拽文件上传功能,即当你将一个文件拖到邮件里后的效果,显示了文件已经传输了多少服务器。

错误处理

即便你从硬盘中读取一个文件也有发生错误的时候。File API 规范[2]定义了四个错误类型:

  • NotFoundError – 文件找不到。
  • SecurityError – 文件或者读取过程出现安全问题。浏览器通常会留有应对此类情况的余地,但是通常载入浏览器的文件是不合法的或者当前有太多的读操作时,就会出现这个错误。
  • NotReadableError – 文件存在但是不可读,类似于没有读取权限。
  • EncodingError – 主要存在于以 data URI 的方式读取文件但结果超出了浏览支持的最大范围。

当读取文件时产生了一个错误,FileReader 对象的 error 属性中将产生一个含有以上错误的实例。规范上就是这么说的。事实上,浏览器将此作为FileError 对象中包含有错误类型的的code属性。每个错误类型对应一个常量值:

  • FileError.NOT_FOUND_ERR 文件找不到.
  • FileError.SECURITY_ERR 安全问题。
  • FileError.NOT_READABLE_ERR 不可读。
  • FileError.ENCODING_ERR 编码问题。
  • FileError.ABORT_ERR  当没有数据可读时调用abort() 。

你可以在event 或者loadend事件中测试以上错误。

var reader = new FileReader();

reader.onloadend = function(event) {
    var contents = event.target.result,
        error    = event.target.error;

    if (error != null) {
        switch (error.code) {
            case error.ENCODING_ERR:
                console.error("Encoding error!");
                break;

            case error.NOT_FOUND_ERR:
                console.error("File not found!");
                break;

            case error.NOT_READABLE_ERR:
                console.error("File could not be read!");
                break;

            case error.SECURITY_ERR:
                console.error("Security issue with file!");
                break;

            default:
                console.error("I have no idea what's wrong!");
        }
    } else {
        progressNode.max = 1;
        progressNode.value = 1;
        console.log("Contents: " + contents);
    }
};

reader.readAsText(file);

下期预告

FileReader 是个全能对象,功能多且与XMLHttpRequest很相似。 通过这三篇文章,你现在已经可能通过JavaScript读取文件和将文件传到服务器了。然而, File API 相对此系列中提到的功能还更加庞大丰富。下篇文章你将会学到另一个更加强大的设计。

参考资料

  1. Progress Events
  2. File API

Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of my employer, my colleagues, Wrox PublishingO’Reilly Publishing, or anyone else. I speak only for myself, not for them.