File API

Editor’s Draft,

More details about this document
This version:
https://w3c.github.io/FileAPI/
Latest published version:
https://www.w3.org/TR/FileAPI/
Feedback:
GitHub
Inline In Spec
Editor:
(Google)
Former Editor:
Arun Ranganathan (Mozilla Corporation)
Tests:
web-platform-tests FileAPI/ (ongoing work)

Abstract

This specification provides an API for representing file objects in web applications, as well as programmatically selecting them and accessing their data. This includes:

Additionally, this specification defines objects to be used within threaded web applications for the synchronous reading of files.

§ 10 Requirements and Use Cases covers the motivation behind this specification.

This API is designed to be used in conjunction with other APIs and elements on the web platform, notably: XMLHttpRequest (e.g. with an overloaded send() method for File or Blob arguments), postMessage(), DataTransfer (part of the drag and drop API defined in [HTML]) and Web Workers. Additionally, it should be possible to programmatically obtain a list of files from the input element when it is in the File Upload state [HTML]. These kinds of behaviors are defined in the appropriate affiliated specifications.

Status of this document

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

This document was published by the Web Applications Working Group as an Editors Draft. This document is intended to become a W3C Recommendation.

Previous discussion of this specification has taken place on two other mailing lists: [email protected] (archive) and [email protected] (archive). Ongoing discussion will be on the [email protected] mailing list.

This draft consists of changes made to the previous Last Call Working Draft. Please send comments to the [email protected] as described above. You can see Last Call Feedback on the W3C Wiki: https://www.w3.org/wiki/Webapps/LCWD-FileAPI-20130912

An implementation report is automatically generated from the test suite.

This document was published by the Web Applications Working Group as a Working Draft. Feedback and comments on this specification are welcome. Please use GitHub issues Historical discussions can be found in the [email protected] archives.

Publication as an Editors Draft does not imply endorsement by W3C and its Members. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 03 November 2023 W3C Process Document.

1. Introduction

This section is informative.

Web applications should have the ability to manipulate as wide as possible a range of user input, including files that a user may wish to upload to a remote server or manipulate inside a rich web application. This specification defines the basic representations for files, lists of files, errors raised by access to files, and programmatic ways to read files. Additionally, this specification also defines an interface that represents "raw data" which can be asynchronously processed on the main thread of conforming user agents. The interfaces and API defined in this specification can be used with other interfaces and APIs exposed to the web platform.

The File interface represents file data typically obtained from the underlying file system, and the Blob interface ("Binary Large Object" - a name originally introduced to web APIs in Google Gears) represents immutable raw data. File or Blob reads should happen asynchronously on the main thread, with an optional synchronous API used within threaded web applications. An asynchronous API for reading files prevents blocking and UI "freezing" on a user agent’s main thread. This specification defines an asynchronous API based on an event model to read and access a File or Blob’s data. A FileReader object provides asynchronous read methods to access that file’s data through event handler content attributes and the firing of events. The use of events and event handlers allows separate code blocks the ability to monitor the progress of the read (which is particularly useful for remote drives or mounted drives, where file access performance may vary from local drives) and error conditions that may arise during reading of a file. An example will be illustrative.

In the example below, different code blocks handle progress, error, and success conditions.
function startRead() {
  // obtain input element through DOM

  var file = document.getElementById('file').files[0];
  if(file){
    getAsText(file);
  }
}

function getAsText(readFile) {

  var reader = new FileReader();

  // Read file into memory as UTF-16
  reader.readAsText(readFile, "UTF-16");

  // Handle progress, success, and errors
  reader.onprogress = updateProgress;
  reader.onload = loaded;
  reader.onerror = errorHandler;
}

function updateProgress(evt) {
  if (evt.lengthComputable) {
    // evt.loaded and evt.total are ProgressEvent properties
    var loaded = (evt.loaded / evt.total);
    if (loaded < 1) {
      // Increase the prog bar length
      // style.width = (loaded * 200) + "px";
    }
  }
}

function loaded(evt) {
  // Obtain the read file data
  var fileString = evt.target.result;
  // Handle UTF-16 file dump
  if(utils.regexp.isChinese(fileString)) {
    //Chinese Characters + Name validation
  }
  else {
    // run other charset test
  }
  // xhr.send(fileString)
}

function errorHandler(evt) {
  if(evt.target.error.name == "NotReadableError") {
    // The file could not be read
  }
}

2. Terminology and Algorithms

When this specification says to terminate an algorithm the user agent must terminate the algorithm after finishing the step it is on. Asynchronous read methods defined in this specification may return before the algorithm in question is terminated, and can be terminated by an abort() call.

The algorithms and steps in this specification use the following mathematical operations:

The term Unix Epoch is used in this specification to refer to the time 00:00:00 UTC on January 1 1970 (or 1970-01-01T00:00:00Z ISO 8601); this is the same time that is conceptually "0" in ECMA-262 [ECMA-262].

The slice blob algorithm given a Blob blob, start, end, and contentType is used to refer to the following steps and returns a new Blob containing the bytes ranging from the start parameter up to but not including the end parameter. It must act as follows:
  1. Let originalSize be blob’s size.

  2. The start parameter, if non-null, is a value for the start point of a slice blob call, and must be treated as a byte-order position, with the zeroth position representing the first byte. User agents must normalize start according to the following:

    1. If start is null, let relativeStart be 0.
    2. If start is negative, let relativeStart be max((originalSize + start), 0).
    3. Otherwise, let relativeStart be min(start, originalSize).
  3. The end parameter, if non-null. is a value for the end point of a slice blob call. User agents must normalize end according to the following:

    1. If end is null, let relativeEnd be originalSize.
    2. If end is negative, let relativeEnd be max((originalSize + end), 0).
    3. Otherwise, let relativeEnd be min(end, originalSize).
  4. The contentType parameter, if non-null, is used to set the ASCII-encoded string in lower case representing the media type of the Blob. User agents must normalize contentType according to the following:

    1. If contentType is null, let relativeContentType be set to the empty string.
    2. Otherwise, let relativeContentType be set to contentType and run the substeps below:
      1. If relativeContentType contains any characters outside the range of U+0020 to U+007E, then set relativeContentType to the empty string and return from these substeps.

      2. Convert every character in relativeContentType to ASCII lowercase.

  5. Let span be max((relativeEnd - relativeStart), 0).

  6. Return a new Blob object S with the following characteristics:

    1. S refers to span consecutive bytes from blob’s associated byte sequence, beginning with the byte at byte-order position relativeStart.
    2. S.size = span.
    3. S.type = relativeContentType.

3. The Blob Interface and Binary Data

A Blob object refers to a byte sequence, and has a size attribute which is the total number of bytes in the byte sequence, and a type attribute, which is an ASCII-encoded string in lower case representing the media type of the byte sequence.

Each Blob must have an internal snapshot state, which must be initially set to the state of the underlying storage, if any such underlying storage exists. Further normative definition of snapshot state can be found for Files.

[Exposed=(Window,Worker), Serializable]
interface Blob {
  constructor(optional sequence<BlobPart> blobParts,
              optional BlobPropertyBag options = {});

  readonly attribute unsigned long long size;
  readonly attribute DOMString type;

  // slice Blob into byte-ranged chunks
  Blob slice(optional [Clamp] long long start,
            optional [Clamp] long long end,
            optional DOMString contentType);

  // read from the Blob.
  [NewObject] ReadableStream stream();
  [NewObject] Promise<USVString> text();
  [NewObject] Promise<ArrayBuffer> arrayBuffer();
  [NewObject] Promise<Uint8Array> bytes();
};

enum EndingType { "transparent", "native" };

dictionary BlobPropertyBag {
  DOMString type = "";
  EndingType endings = "transparent";
};

typedef (BufferSource or Blob or USVString) BlobPart;

Blob objects are serializable objects. Their serialization steps, given value and serialized, are:

  1. Set serialized.[[SnapshotState]] to value’s snapshot state.

  2. Set serialized.[[ByteSequence]] to value’s underlying byte sequence.

Their deserialization step, given serialized and value, are:

  1. Set value’s snapshot state to serialized.[[SnapshotState]].

  2. Set value’s underlying byte sequence to serialized.[[ByteSequence]].

A Blob blob has an associated get stream algorithm, which runs these steps:
  1. Let stream be a new ReadableStream created in blob’s relevant Realm.

  2. Set up stream with byte reading support.

  3. Run the following steps in parallel:

    1. While not all bytes of blob have been read:

      1. Let bytes be the byte sequence that results from reading a chunk from blob, or failure if a chunk cannot be read.

      2. Queue a global task on the file reading task source given blob’s relevant global object to perform the following steps:

        1. If bytes is failure, then error stream with a failure reason and abort these steps.

        2. Let chunk be a new Uint8Array wrapping an ArrayBuffer containing bytes. If creating the ArrayBuffer throws an exception, then error stream with that exception and abort these steps.

        3. Enqueue chunk in stream.

      We need to specify more concretely what reading from a Blob actually does, what possible errors can happen, perhaps something about chunk sizes, etc.

  4. Return stream.

3.1. Constructors

The Blob() constructor can be invoked with zero or more parameters. When the Blob() constructor is invoked, user agents must run the following steps:
  1. If invoked with zero parameters, return a new Blob object consisting of 0 bytes, with size set to 0, and with type set to the empty string.

  2. Let bytes be the result of processing blob parts given blobParts and options.

  3. If the type member of the options argument is not the empty string, run the following sub-steps:

    1. Let t be the type dictionary member. If t contains any characters outside the range U+0020 to U+007E, then set t to the empty string and return from these substeps.

    2. Convert every character in t to ASCII lowercase.

  4. Return a Blob object referring to bytes as its associated byte sequence, with its size set to the length of bytes, and its type set to the value of t from the substeps above.

3.1.1. Constructor Parameters

The Blob() constructor can be invoked with the parameters below:

A blobParts sequence
which takes any number of the following types of elements, and in any order:
An optional BlobPropertyBag
which takes these optional members:
To process blob parts given a sequence of BlobPart's parts and BlobPropertyBag options, run the following steps:
  1. Let bytes be an empty sequence of bytes.

  2. For each element in parts:

    1. If element is a USVString, run the following substeps:

      1. Let s be element.

      2. If the endings member of options is "native", set s to the result of converting line endings to native of element.

      3. Append the result of UTF-8 encoding s to bytes.

        Note: The algorithm from WebIDL [WebIDL] replaces unmatched surrogates in an invalid utf-16 string with U+FFFD replacement characters. Scenarios exist when the Blob constructor may result in some data loss due to lost or scrambled character sequences.

    2. If element is a BufferSource, get a copy of the bytes held by the buffer source, and append those bytes to bytes.

    3. If element is a Blob, append the bytes it represents to bytes.

      Note: The type of the Blob array element is ignored and will not affect type of returned Blob object.

  3. Return bytes.

To convert line endings to native in a string s, run the following steps:
  1. Let native line ending be the code point U+000A LF.

  2. If the underlying platform’s conventions are to represent newlines as a carriage return and line feed sequence, set native line ending to the code point U+000D CR followed by the code point U+000A LF.

  3. Set result to the empty string.

  4. Let position be a position variable for s, initially pointing at the start of s.

  5. Let token be the result of collecting a sequence of code points that are not equal to U+000A LF or U+000D CR from s given position.

  6. Append token to result.

  7. While position is not past the end of s:

    1. If the code point at position within s equals U+000D CR:

      1. Append native line ending to result.

      2. Advance position by 1.

      3. If position is not past the end of s and the code point at position within s equals U+000A LF advance position by 1.

    2. Otherwise if the code point at position within s equals U+000A LF, advance position by 1 and append native line ending to result.

    3. Let token be the result of collecting a sequence of code points that are not equal to U+000A LF or U+000D CR from s given position.

    4. Append token to result.

  8. Return result.

Examples of constructor usage follow.
// Create a new Blob object

var a = new Blob();

// Create a 1024-byte ArrayBuffer
// buffer could also come from reading a File

var buffer = new ArrayBuffer(1024);

// Create ArrayBufferView objects based on buffer

var shorts = new Uint16Array(buffer, 512, 128);
var bytes = new Uint8Array(buffer, shorts.byteOffset + shorts.byteLength);

var b = new Blob(["foobarbazetcetc" + "birdiebirdieboo"], {type: "text/plain;charset=utf-8"});

var c = new Blob([b, shorts]);

var a = new Blob([b, c, bytes]);

var d = new Blob([buffer, b, c, bytes]);

3.2. Attributes

size, of type unsigned long long, readonly
Returns the size of the byte sequence in number of bytes. On getting, conforming user agents must return the total number of bytes that can be read by a FileReader or FileReaderSync object, or 0 if the Blob has no bytes to be read.
type, of type DOMString, readonly
The ASCII-encoded string in lower case representing the media type of the Blob. On getting, user agents must return the type of a Blob as an ASCII-encoded string in lower case, such that when it is converted to a byte sequence, it is a parsable MIME type, or the empty string – 0 bytes – if the type cannot be determined.

The type attribute can be set by the web application itself through constructor invocation and through the slice() call; in these cases, further normative conditions for this attribute are in § 3.1 Constructors, § 4.1 Constructor, and § 3.3.1 The slice() method respectively. User agents can also determine the type of a Blob, especially if the byte sequence is from an on-disk file; in this case, further normative conditions are in the file type guidelines.

Note: The type t of a Blob is considered a parsable MIME type, if performing the parse a MIME type algorithm to a byte sequence converted from the ASCII-encoded string representing the Blob object’s type does not return failure.

Note: Use of the type attribute informs the package data algorithm and determines the Content-Type header when fetching blob URLs.

3.3. Methods and Parameters

3.3.1. The slice() method

The slice() method returns a new Blob object with bytes ranging from the optional start parameter up to but not including the optional end parameter, and with a type attribute that is the value of the optional contentType parameter. It must act as follows:
  1. Let sliceStart, sliceEnd, and sliceContentType be null.

  2. If start is given, set sliceStart to start.

  3. If end is given, set sliceEnd to end.

  4. If contentType is given, set sliceContentType to contentType.

  5. Return the result of slice blob given this, sliceStart, sliceEnd, and sliceContentType.

The examples below illustrate the different types of slice() calls possible. Since the File interface inherits from the Blob interface, examples are based on the use of the File interface.
// obtain input element through DOM

var file = document.getElementById('file').files[0];
if(file)
{
  // create an identical copy of file
  // the two calls below are equivalent

  var fileClone = file.slice();
  var fileClone2 = file.slice(0, file.size);

  // slice file into 1/2 chunk starting at middle of file
  // Note the use of negative number

  var fileChunkFromEnd = file.slice(-(Math.round(file.size/2)));

  // slice file into 1/2 chunk starting at beginning of file

  var fileChunkFromStart = file.slice(0, Math.round(file.size/2));

  // slice file from beginning till 150 bytes before end

  var fileNoMetadata = file.slice(0, -150, "application/experimental");
}

3.3.2. The stream() method

The stream() method, when invoked, must return the result of calling get stream on this.

3.3.3. The text() method

The text() method, when invoked, must run these steps:

  1. Let stream be the result of calling get stream on this.

  2. Let reader be the result of getting a reader from stream. If that threw an exception, return a new promise rejected with that exception.

  3. Let promise be the result of reading all bytes from stream with reader.

  4. Return the result of transforming promise by a fulfillment handler that returns the result of running UTF-8 decode on its first argument.

Note: This is different from the behavior of readAsText() to align better with the behavior of Fetch’s text(). Specifically this method will always use UTF-8 as encoding, while FileReader can use a different encoding depending on the blob’s type and passed in encoding name.

3.3.4. The arrayBuffer() method

The arrayBuffer() method, when invoked, must run these steps:

  1. Let stream be the result of calling get stream on this.

  2. Let reader be the result of getting a reader from stream. If that threw an exception, return a new promise rejected with that exception.

  3. Let promise be the result of reading all bytes from stream with reader.

  4. Return the result of transforming promise by a fulfillment handler that returns a new ArrayBuffer whose contents are its first argument.

3.3.5. The bytes() method

The bytes() method, when invoked, must run these steps:

  1. Let stream be the result of calling get stream on this.

  2. Let reader be the result of getting a reader from stream. If that threw an exception, return a new promise rejected with that exception.

  3. Let promise be the result of reading all bytes from stream with reader.

  4. Return the result of transforming promise by a fulfillment handler that returns a new Uint8Array wrapping an ArrayBuffer containing its first argument.

4. The File Interface

A File object is a Blob object with a name attribute, which is a string; it can be created within the web application via a constructor, or is a reference to a byte sequence from a file from the underlying (OS) file system.

If a File object is a reference to a byte sequence originating from a file on disk, then its snapshot state should be set to the state of the file on disk at the time the File object is created.

Note: This is a non-trivial requirement to implement for user agents, and is thus not a must but a should [RFC2119]. User agents should endeavor to have a File object’s snapshot state set to the state of the underlying storage on disk at the time the reference is taken. If the file is modified on disk following the time a reference has been taken, the File's snapshot state will differ from the state of the underlying storage. User agents may use modification time stamps and other mechanisms to maintain snapshot state, but this is left as an implementation detail.

When a File object refers to a file on disk, user agents must return the type of that file, and must follow the file type guidelines below:

[Exposed=(Window,Worker), Serializable]
interface File : Blob {
  constructor(sequence<BlobPart> fileBits,
              USVString fileName,
              optional FilePropertyBag options = {});
  readonly attribute DOMString name;
  readonly attribute long long lastModified;
};

dictionary FilePropertyBag : BlobPropertyBag {
  long long lastModified;
};

File objects are serializable objects. Their serialization steps, given value and serialized, are:

  1. Set serialized.[[SnapshotState]] to value’s snapshot state.

  2. Set serialized.[[ByteSequence]] to value’s underlying byte sequence.

  3. Set serialized.[[Name]] to the value of value’s name attribute.

  4. Set serialized.[[LastModified]] to the value of value’s lastModified attribute.

Their deserialization steps, given value and serialized, are:

  1. Set value’s snapshot state to serialized.[[SnapshotState]].

  2. Set value’s underlying byte sequence to serialized.[[ByteSequence]].

  3. Initialize the value of value’s name attribute to serialized.[[Name]].

  4. Initialize the value of value’s lastModified attribute to serialized.[[LastModified]].

4.1. Constructor

The File constructor is invoked with two or three parameters, depending on whether the optional dictionary parameter is used. When the File() constructor is invoked, user agents must run the following steps:
  1. Let bytes be the result of processing blob parts given fileBits and options.

  2. Let n be the fileName argument to the constructor.

    Note: Underlying OS filesystems use differing conventions for file name; with constructed files, mandating UTF-16 lessens ambiquity when file names are converted to byte sequences.

  3. Process FilePropertyBag dictionary argument by running the following substeps:

    1. If the type member is provided and is not the empty string, let t be set to the type dictionary member. If t contains any characters outside the range U+0020 to U+007E, then set t to the empty string and return from these substeps.

    2. Convert every character in t to ASCII lowercase.

    3. If the lastModified member is provided, let d be set to the lastModified dictionary member. If it is not provided, set d to the current date and time represented as the number of milliseconds since the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]).

      Note: Since ECMA-262 Date objects convert to long long values representing the number of milliseconds since the Unix Epoch, the lastModified member could be a Date object [ECMA-262].

  4. Return a new File object F such that:

    1. F refers to the bytes byte sequence.

    2. F.size is set to the number of total bytes in bytes.

    3. F.name is set to n.

    4. F.type is set to t.

    5. F.lastModified is set to d.

4.1.1. Constructor Parameters

The File() constructor can be invoked with the parameters below:

A fileBits sequence
which takes any number of the following elements, and in any order:
A fileName parameter
A USVString parameter representing the name of the file; normative conditions for this constructor parameter can be found in § 4.1 Constructor.
An optional FilePropertyBag dictionary
which in addition to the members of BlobPropertyBag takes one member:
  • An optional lastModified member, which must be a long long; normative conditions for this member are provided in § 4.1 Constructor.

4.2. Attributes

name, of type DOMString, readonly
The name of the file. On getting, this must return the name of the file as a string. There are numerous file name variations and conventions used by different underlying OS file systems; this is merely the name of the file, without path information. On getting, if user agents cannot make this information available, they must return the empty string. If a File object is created using a constructor, further normative conditions for this attribute are found in § 4.1 Constructor.
lastModified, of type long long, readonly
The last modified date of the file. On getting, if user agents can make this information available, this must return a long long set to the time the file was last modified as the number of milliseconds since the Unix Epoch. If the last modification date and time are not known, the attribute must return the current date and time as a long long representing the number of milliseconds since the Unix Epoch; this is equivalent to Date.now() [ECMA-262]. If a File object is created using a constructor, further normative conditions for this attribute are found in § 4.1 Constructor.

The File interface is available on objects that expose an attribute of type FileList; these objects are defined in HTML [HTML]. The File interface, which inherits from Blob, is immutable, and thus represents file data that can be read into memory at the time a read operation is initiated. User agents must process reads on files that no longer exist at the time of read as errors, throwing a NotFoundError exception if using a FileReaderSync on a Web Worker [Workers] or firing an error event with the error attribute returning a NotFoundError.

In the examples below, metadata from a file object is displayed meaningfully, and a file object is created with a name and a last modified date.
var file = document.getElementById("filePicker").files[0];
var date = new Date(file.lastModified);
println("You selected the file " + file.name + " which was modified on " + date.toDateString() + ".");

...

// Generate a file with a specific last modified date

var d = new Date(2013, 12, 5, 16, 23, 45, 600);
var generatedFile = new File(["Rough Draft ...."], "Draft1.txt", {type: "text/plain", lastModified: d})

...

5. The FileList Interface

Note: The FileList interface should be considered "at risk" since the general trend on the Web Platform is to replace such interfaces with the Array platform object in ECMAScript [ECMA-262]. In particular, this means syntax of the sort filelist.item(0) is at risk; most other programmatic use of FileList is unlikely to be affected by the eventual migration to an Array type.

This interface is a list of File objects.

[Exposed=(Window,Worker), Serializable]
interface FileList {
  getter File? item(unsigned long index);
  readonly attribute unsigned long length;
};

FileList objects are serializable objects. Their serialization steps, given value and serialized, are:

  1. Set serialized.[[Files]] to an empty list.

  2. For each file in value, append the sub-serialization of file to serialized.[[Files]].

Their deserialization step, given serialized and value, are:

  1. For each file of serialized.[[Files]], add the sub-deserialization of file to value.

Sample usage typically involves DOM access to the <input type="file"> element within a form, and then accessing selected files.
// uploadData is a form element
// fileChooser is input element of type 'file'
var file = document.forms['uploadData']['fileChooser'].files[0];

// alternative syntax can be
// var file = document.forms['uploadData']['fileChooser'].files.item(0);

if(file)
{
  // Perform file ops
}

5.1. Attributes

length, of type unsigned long, readonly
must return the number of files in the FileList object. If there are no files, this attribute must return 0.

5.2. Methods and Parameters

item(index)
must return the indexth File object in the FileList. If there is no indexth File object in the FileList, then this method must return null.

index must be treated by user agents as value for the position of a File object in the FileList, with 0 representing the first file. Supported property indices are the numbers in the range zero to one less than the number of File objects represented by the FileList object. If there are no such File objects, then there are no supported property indices.

Note: The HTMLInputElement interface has a readonly attribute of type FileList, which is what is being accessed in the above example. Other interfaces with a readonly attribute of type FileList include the DataTransfer interface.

6. Reading Data

6.1. The File Reading Task Source

This specification defines a new generic task source called the file reading task source, which is used for all tasks that are queued in this specification to read byte sequences associated with Blob and File objects. It is to be used for features that trigger in response to asynchronously reading binary data.

6.2. The FileReader API

[Exposed=(Window,Worker)]
interface FileReader: EventTarget {
  constructor();
  // async read methods
  undefined readAsArrayBuffer(Blob blob);
  undefined readAsBinaryString(Blob blob);
  undefined readAsText(Blob blob, optional DOMString encoding);
  undefined readAsDataURL(Blob blob);

  undefined abort();

  // states
  const unsigned short EMPTY = 0;
  const unsigned short LOADING = 1;
  const unsigned short DONE = 2;

  readonly attribute unsigned short readyState;

  // File or Blob data
  readonly attribute (DOMString or ArrayBuffer)? result;

  readonly attribute DOMException? error;

  // event handler content attributes
  attribute EventHandler onloadstart;
  attribute EventHandler onprogress;
  attribute EventHandler onload;
  attribute EventHandler onabort;
  attribute EventHandler onerror;
  attribute EventHandler onloadend;
};

A FileReader has an associated state, that is "empty", "loading", or "done". It is initially "empty".

A FileReader has an associated result (null, a DOMString or an ArrayBuffer). It is initially null.

A FileReader has an associated error (null or a DOMException). It is initially null.

The FileReader() constructor, when invoked, must return a new FileReader object.

The readyState attribute’s getter, when invoked, switches on this's state and runs the associated step:

"empty"

Return EMPTY

"loading"

Return LOADING

"done"

Return DONE

The result attribute’s getter, when invoked, must return this's result.

The error attribute’s getter, when invoked, must return this's error.

A FileReader fr has an associated read operation algorithm, which given blob, a type and an optional encodingName, runs the following steps:
  1. If fr’s state is "loading", throw an InvalidStateError DOMException.

  2. Set fr’s state to "loading".

  3. Set fr’s result to null.

  4. Set fr’s error to null.

  5. Let stream be the result of calling get stream on blob.

  6. Let reader be the result of getting a reader from stream.

  7. Let bytes be an empty byte sequence.

  8. Let chunkPromise be the result of reading a chunk from stream with reader.

  9. Let isFirstChunk be true.

  10. In parallel, while true:

    1. Wait for chunkPromise to be fulfilled or rejected.

    2. If chunkPromise is fulfilled, and isFirstChunk is true, queue a task to fire a progress event called loadstart at fr.

      We might change loadstart to be dispatched synchronously, to align with XMLHttpRequest behavior. [Issue #119]

    3. Set isFirstChunk to false.

    4. If chunkPromise is fulfilled with an object whose done property is false and whose value property is a Uint8Array object, run these steps:

      1. Let bs be the byte sequence represented by the Uint8Array object.

      2. Append bs to bytes.

      3. If roughly 50ms have passed since these steps were last invoked, queue a task to fire a progress event called progress at fr.

      4. Set chunkPromise to the result of reading a chunk from stream with reader.

    5. Otherwise, if chunkPromise is fulfilled with an object whose done property is true, queue a task to run the following steps and abort this algorithm:

      1. Set fr’s state to "done".

      2. Let result be the result of package data given bytes, type, blob’s type, and encodingName.

      3. If package data threw an exception error:

        1. Set fr’s error to error.

        2. Fire a progress event called error at fr.

      4. Else:

        1. Set fr’s