FileReader objects for loading local files on local web pages

I think it'd be great to have a *simple to use* API specifically for  
loading local files on local pages (file://).

There's already DOM3 Load and Save, but few want anything to do with that  
(it's not simple for one).

There's also the previous document.load, but it's half broken in Opera,  
not implemented in Safari (unless things have changed recently) and isn't  
very good with error handling in Firefox. In other words, it's a mess.

There's also XHR, but it doesn't really support file://. Firefox, Safari  
and Opera have implemented file:// access, but they don't all use the same  
security restrictions, don't all handle errors the same and don't simulate  
HTTP status codes for local file operations. This makes loading a local  
file a mess with XHR and makes it so you have to use different conditions  
in the readystatechange callback compared to HTTP. It's possible though to  
load a local file, but it's impossible to differentiate between the  
different type of errors (like file not found, access denied, read error,  
cross-origin attempt etc.). IE doesn't support accessing file:// with XHR,  
so can't comment on it.

With that said, here's an idea (and what I'd like):

There should be a FileReader interface that supports just load(fileURI)  
and error and load events (via onerror/onload or through addEventListener).

Then, there'd be different types of reader objects that inherit from  
FileReader. The two that I'm suggesting are TextFileReader and  
BinaryFileReader. (I believe it's better to separate functionality into 2  
separate objects instead of mixing things.)

They would work like this:

var treader = new TextFileReader();
treader.addEventListener("load", function(e) {
     alert(e.text);
}, false);
treader.addEventListener("error", function(e) {
     alert(e.error.code);
}, false);

and

var breader = new BinaryFileReader();
breader.addEventListener("load", function(e) {
     alert(e.bytes);
}, false);
breader.addEventListener("error", function(e) {
     alert(e.error.code);
}, false);

'load' does not fire if there's an error. 'error' fires for the first  
error (the code would return after the first error).

Nothing throws an exception. The author can do that in the error handler  
if desired. That way, you can do things how you want. You can also easily  
wrap things to use callback functions instead if you really wanted.

For the load event objects, there'd be a BinaryReaderLoadEvent that has a  
bytes getter and a TextReaderLoadEvent that has a text getter. They would  
inherit from Event. (I like these separate instead of having one event  
object that has both .text and .bytes where one might be null or something)

For the error event object, there'd be a FileReaderErrorEvent object that  
has an error getter that returns a FileReaderException object, which  
inherits from DOMException and has a code getter that returns different  
error codes for file not found etc. (that would have to be agreed upon).

It'd all look something like this (don't know idl stuff very well, but it  
should be good enough to get the idea across)

interface FileReader {
     onload
     onerror
     addEventListener
     void load(uri);
};

interface TestFileReader : FileReader {

};

interface BinaryFileReader : FileReader {

};

interface TextFileReaderLoadEvent : Event {
     readonly attribute string text;
};

interface BinaryFileReaderLoadEvent : Event {
     readonly attribute array bytes;
};

interface FileReaderException : DOMException {
     readyonly attribute unsigned short code;
};
// const unsigned short NOT_FOUND_ERR = 1;
// const unsigned short ACCESS_DENIED_ERR = 2;
// const unsigned short ORIGIN_VIOLATION_ERR = 3;
// const unsigned short READ_ERROR_ERR = 4;

interface FileReaderErrorEvent : Event {
     readonly attribute FileReaderException error;
};

All the objects should be exposed on the window object so one can  
extend/override them with prototypes and getters etc. if desired.

There'd by no sync version, only async like the examples above.

I don't think progress events and reading the file in chunks are needed  
for this simple API.

I also specifically left out xml parsing, as that can be done with  
DOMParser once you get the file text. (If DOMParser has shortcomings, it  
should be improved.)

As for the encoding used for TextFileReader when reading the file, not  
sure. I expect it to work just like document.load, XHR (with file://) and  
DOM3 Load & Save work.

If you try to use a FileReader on a protocol besides file://, you'd get  
the ORIGIN_VIOLATION_ERR in the error listener. (Note that  
file://bark/dir/test.html <- and file://meow/dir/file.txt would be a  
cross-origin violation for example.)

You'd also get the ORIGIN_VIOLATION_ERR in the error listener if you try  
to use a FileReader on a non-file:// page.

Error handling is very important. -- Not for debugging, but for the  
user-informative 'why things went wrong'. I absolutely hate it when a  
generic exception is thrown and you can't tell if the file was not found  
or if there's a cross-origin security error for example (like Firefox does  
for XHR's open() when you specify a URI to a non-existent file). It makes  
it difficult to give info on what went wrong.

That's the basic idea.

And, there could always be other readers like Base64FileReader and  
DataURIFileReader later on. There could even be an XMLFileReader (if you  
didn't want to create one yourself by wrapping the text reader and using  
DOMParser).

I would also expect FileWriter objects, but saving that for later.

Now, with regards to the FileAPI, I see that it has objects and methods  
for reading files. However, I don't currently see a way to get a files  
object that has an entry that points to a file without using <input  
type="file">.

If there was a way though to load a local file on a local web page  
directly (without user-interaction and without <input type="file">) in JS  
with the FileAPI, maybe none of this is needed.

Finally, I have to say that for the treader and breader examples above, I  
really love the idea of things working just like that.

-- 
Michael

Received on Tuesday, 18 August 2009 02:38:30 UTC