Copyright © 2013 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved. W3C liability, trademark and document use rules apply.
This specification provides an API for representing binary and string data in web applications as a Stream object,
as well as programmatically building and reading its contents. This includes:
Stream interface, which represents a sequence of data which can be read only once and provides APIs for reading, writing, and piping the data.StreamConsumeResult interface, which represents a chunk of content read from a stream.StreamReadType interface, which represents how the Stream is currently being read.Stream object.Stream.Stream.
This API is designed to be used in conjunction with other APIs and elements on the web platform, notably:
File [FILE-API],
XMLHttpRequest
(e.g. with an overloaded send() method
and response object for Stream objects) [XMLHTTPREQUEST2],
postMessage, and
Web Workers [WEBWORKERS].
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.
This document is not complete. It is subject to major changes and, while early experimentations are encouraged, it is therefore not intended for implementation.
This document was published by the W3C Web Applications (WebApps) as a Working Draft.
This document is intended to become a W3C Recommendation.
If you wish to make comments regarding this document, please send them to
public-webapps@w3.org
(subscribe,
archive)
with a Subject prefix of [streams-api].
If you wish to submit a bug, please use
Bugzilla.
All comments and bug reports are welcome.
Publication as a Working Draft does not imply endorsement by the W3C Membership. 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 5 February 2004 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 section is non-normative.
Web applications should have the ability to acquire and manipulate data in a wide variety of forms, including as a sequence of data made available over time. This specification defines the basic representation for Streams, errors raised by Streams, and programmatic ways to create, read, and write to Streams.
The Stream interface represents binary data which can be obtained over time and read once. A Stream can come from API producers such as XMLHttpRequest, or can
be built using the Stream constructor.
The Stream interface provides a read method for reading the data
from a Stream as a StreamConsumeResult, which provides the data as a Blob, ArrayBuffer, or as DOMString,
and should happen asynchronously on the user agent’s main thread. Additionally, the stream can also be used in API consumers such as a media element.
The Stream interface also provides a write method for writing data
to a Stream as a Blob, ArrayBuffer, or as DOMString,
and should happen asynchronously on the user agent’s main thread.
An asynchronous API for reading Streams prevents blocking and UI “freezing” on a user agent’s main thread.
This specification defines an asynchronous API to access a Stream. Error conditions that may arise during reading of a Stream will be handled by a reject callback set to the promise returned by the read() method. An example will be illustrative.
In the example below, different code blocks handle progress, error, and success conditions.
The example demonstrates how to read a chunk of data from a Stream using read. The Stream may of come from a producer such as XMLHttpRequest. Additionally, it demonstrates how to read a stream until an EOF is encountered.
// Read the first 1024 bytes of the stream as UTF-8
stream.readEncoding = "UTF-8";
stream.readType = "arraybuffer";
Promise readPromise = stream.read(1024);
readPromise.then(
function(result) {
console.log("Loaded" + result.size + " bytes");
// Handle result.data
},
function(error) {
// Handle error
}
);
// Read data from the stream repeatedly
function readUntilEof() {
stream.read(1024).then(
function(result) {
// Handle read data
someProcessFunction(result.data);
// Print progress
someReportFunction(result.size);
if (!result.eof) {
readUntilEof();
}
},
function(error) {
// Handle error
}
);
}
In the example below, different code blocks handle progress, error, and success conditions.
The example below demonstrates how to obtain a Stream from XMLHttpRequest to begin playing a large video in readystate 3. The example takes the Stream from a producer, XMLHttpRequest, and gives to a consumer, the video tag.
function handler() {
if(this.readyState == this.LOADING) {
var theStream = this.response;
var streamURL = URL.createObjectURL(theStream);
document.getElementById("myVideoTag").src = streamURL;
}
}
var client = new XMLHttpRequest();
client.onreadystatechange = handler;
client.setRequestHeader('customHeader', 'value');
client.setRequestHeader('customHeader2', 'value2');
client.open("GET", "myvideo.h264");
client.responseType = "stream";
client.send();
In addition to reading a Stream, this specification introduces a programatic way to write data to a Stream.
The Stream interface provides a write() method which allows applications to build the data to be read by a Stream consumer by appending the data to an internal buffer.
write() supports appending data as a Blob, ArrayBuffer, or DOMString to the buffer.
The example below demonstrates how to use write() to load a Stream into the audio tag, whose data could be processed and built dynamically at read time.
var theStream = new Stream("audio/mp3");
function writeData(){
var moreData;
// Do work to create more data to place into the stream
// If we have no more data to process and place in the stream, we close
if (moreData == null){
theStream.close();
} else{
theStream.write(moreData).then(
function () {
writeData();
},
function (error) {
// Handle error
}
);
}
}
var streamURL = URL.createObjectURL(theStream);
document.getElementById('audioTag').src = streamURL;
writeData();
This section introduces the Stream interface, as well as accompanying interfaces required as part of the Stream implementation. This includes a constructor to build a Stream, as well as methods to read, write, skip, pipe, and close streams.
This interface represents a raw sequence of linear data which can be read only once over time. It provides a type attribute which represents the type of data in the Stream. It also provides the ability to read and write the contents of the Stream.
A Stream is an object that:
A Stream object has an associated write closed flag. It is set when the close() method is called. This flag is internal, so scripts cannot access it directly.
A Stream object has an associated write pending flag. It is set when the write() method is called and unset when it's completed. This flag is internal, so scripts cannot access it directly.
A Stream object has an associated read pending flag. It is set when read(), skip() or pipe() method is called and unset when it's completed. This flag is internal, so scripts cannot access it directly.
A Stream holds a sequence of bytes possibly terminated by a terminator. Writing bytes to a Stream means appending the bytes to the sequence. Terminating a Stream means appending a terminator to the sequence. Reading bytes from a Stream pops bytes from the head of the sequence. If a terminator is encountered while reading bytes from a Stream, it is said the EOF is reached. This sequence is internal, so scripts cannot access it directly.
[ Constructor (optional DOMString type)]
interface Stream {
readonly attribute DOMString type;
attribute StreamReadType readType;
attribute DOMString readEncoding;
Promise write ((DOMString or ArrayBufferView or Blob) data);
void close ();
Promise read (optional [Clamp] unsigned long long size);
Promise skip (![Clamp] unsigned long long size);
Promise pipe (!(Stream or Stream[]) destination, optional [Clamp] unsigned long long size);
};readEncoding of type DOMString,
A DOMString that represents the label of an encoding [EncodingDetermination]. If set, it will be used as part of the encoding determination used when processing a read call. If not set, it will return the empty string.
readType of type StreamReadType,
Returns the type of the last read operation taken on the Stream. On getting, conforming user agents must return the type of the last read operation. If no read operation has taken place and the readType was not set, then return the empty string. This can be set to the empty string (default), "arraybuffer", "blob" and "text" to change the type of the read operation.
type of type DOMString, readonly Stream,
expressed as an RFC2046 MIME type [RFC2046].
Conforming user agents SHOULD return the MIME type of the Stream, if it is known.
If conforming user agents cannot determine the media type of the Stream, they MUST return the empty string.
A string is a valid MIME type if it matches the media-type token defined in section 3.7 "Media Types" of RFC 2616 [HTTP11].
close
This method closes the Stream and does not allow any future writes. This is an irreversible operation; once a Stream has been closed, it cannot be written to again.
When all data has been read from the Stream on which close() has been called, i.e. EOF is reached, it resolves the Promise returned by read() with a StreamConsumeResult with the eof attribute set to true.
The user agent must run the steps below:
Stream has been neutered, throw an "InvalidStateError" [DOM4] exception and terminate these steps.InvalidStateError" [DOM4] exception and terminate these steps.InvalidStateError" [DOM4] exception and terminate these steps.Stream and terminate these steps.pipe
This method transfers data from the Stream to another Stream.
This method takes a destinations and optionally a size.
Another read(), skip() or pipe() call must not be made until the returned Promise is resolved or rejected.
Resolution of the returned Promise doesn't necessarily mean that the data transferred to the destination Stream has been successfully read from the Stream.
The user agent must run the steps below:
Stream has been neutered, throw an "InvalidStateError" [DOM4] exception and terminate these steps.InvalidStateError" [DOM4] exception and terminate these steps.size is specified but is 0, throw a "SyntaxError" [DOM4] exception and terminate these steps.destinations is a Stream, let destinations instead be an array consisting of just that Stream.readPromise be a new promise.readPromise, but continue to process the steps in this algorithm.size is specified, read data from the stream until size bytes are read.destinations.destinations, neuter the Stream, let exception be an "InvalidStateError" exception and run Reject(readPromise, exception) as specified in the promises spec and terminate these steps.result be a newly created StreamConsumeResult object.result to true.result set to false.result to the total size of the read data.Resolve(readPromise, result) as specified in the promises spec.| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| destination | | ✘ | ✘ | Destination Stream. |
| size | | ✘ | ✔ | Number of bytes to transfer. |
read
This method reads data from the Stream.
This method takes an optional size which represents the number of bytes to be read.
Another read(), skip() or pipe() call must not be made until the returned Promise is resolved or rejected.
The user agent must run the steps below (unless otherwise indicated):
Stream has been neutered, throw an "InvalidStateError" [DOM4] exception and terminate these steps.InvalidStateError" [DOM4] exception and terminate these steps.size is specified but is 0, throw a "SyntaxError" [DOM4] exception and terminate these steps.readPromise be a new promise.
readPromise, but continue to process the steps in this algorithm.size is specified, read data from the Stream until size bytes are read.Stream, let exception be an "InvalidStateError" [DOM4] exception and run Reject(readPromise, exception) as specified in the promises spec and terminate these steps.result be a newly created StreamConsumeResult.result to true.result to false.result to the result of executing the steps below.
text"
readEncoding.
result be result of decoding the data read using fallback encoding charset.
blob"
result be a blob created from the read data
arraybuffer"
result be an array buffer created from the read data
Resolve(readPromise, result) as specified in the promises spec.
| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| size | | ✘ | ✔ | Number of bytes to read. |
skip
This method reads data from the Stream and ignore them.
This method takes optionally a size which represents the number of bytes to be read and ignored.
Another read(), skip() or pipe() call must not be made until the returned Promise is resolved or rejected.
The user agent must run the steps below:
Stream has been neutered, throw an "InvalidStateError" [DOM4] exception and terminate these steps.InvalidStateError" [DOM4] exception and terminate these steps.size is specified but is 0, throw a "SyntaxError" [DOM4] exception and terminate these steps.readPromise be a new promise.skip() method with readPromise, but continue to process the steps in this algorithm.size is specified, read data from the Stream until size bytes are read.Stream, let exception be an "InvalidStateError" [DOM4] exception and run Reject(readPromise, exception) as specified in the promises spec and terminate these steps.result be a newly created StreamConsumeResult object.result to true.result set to false.result to the size of the read data in bytes.Resolve(readPromise, result) as specified in the promises spec.| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| size | | ✘ | ✘ | Number of bytes to read and ignore. |
write
This method writes data to the Stream.
Another write() or close() call must not be made until the returned Promise is resolved or rejected.
Resolution of the returned Promise doesn't necessarily mean that the data written has been successfully read.
The user agent must run the steps below (unless otherwise indicated):
Stream has been neutered, throw an "InvalidStateError" [DOM4] exception and terminate these steps.InvalidStateError" [DOM4] exception and terminate these steps.InvalidStateError" [DOM4] exception and terminate these steps.writePromise be a new promise.writePromise, but continue to process the steps in this algorithm.data:
ArrayBufferViewrawData be the raw data represented by the Blob object.BlobrawData be the data stored in the section of the buffer described by the ArrayBuffer object that the ArrayBufferView object references.DOMStringrawData be the result of encoding data to binary data using the encoding determined by the [EncodingDetermination] rawData to the Stream.
Stream, let exception be an "InvalidStateError" [DOM4] exception and run Reject(writePromise, exception) as specified in the promises spec and terminate this algorithm.
Resolve(writePromise, undefined) as specified in the promises spec. Implementations may delay this step if appropriate.
| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| data | | ✘ | ✘ | Data to write. |
interface StreamConsumeResult {
readonly attribute boolean eof;
readonly attribute any data;
readonly attribute unsigned long long size;
};data of type any, readonly eof of type boolean, readonly Streamsize of type unsigned long long, readonly enum StreamReadType {
"blob",
"arraybuffer",
"text"
};| Enumeration description | |
|---|---|
blob | Read operations should return data as a Blob |
arraybuffer | Read operations should return data as an ArrayBuffer |
text | Read operations should return data as a DOMString |
To reference a Stream, the same URI used for Blobs and Files in
6.7. A URI for Blob and File reference of
the File API specification should be used. [FILE-API]
The definitions of Origin, Lifetime, Referencing, and Dereferencing of a Blob should be applied to a Stream.
A Stream URI is a Blob URI that is referencing a Stream.
These URIs are created and revoked using methods exposed on the URL object,
as defined in 6.7.5. Creating and Revoking a Blob URI
of the File API specification. [FILE-API]
URL.createObjectURL and URL.revokeObjectURL should both be extended as follows:
interface URL {
static DOMString? createObjectURL ((Blob or Stream) object);
static DOMString? createFor ((Blob or Stream) object);
static void revokeObjectURL (DOMString url);
};createFor, staticThe extension onto createFor should have the following steps added.
Returns a unique Blob URL each time it is called on a valid object argument, which is a non-null Stream in scope of the global object's URL property from which this static method is called. Blob URLs created with this method are said to be auto-revoking since user-agents are responsible for the revocation of Blob URLs created with this method, subject to the lifetime stipulation for Blob URLs. This method must act as follows:
Stream argument that is NOT valid, then user agents must return null.Stream argument,
user agents must run the following sub-steps:
Stream is set, return null.Stream.Blob URI that can be used to dereference the stream argument.| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| object | | ✘ | ✘ |
, nullablecreateObjectURL, staticThe extension onto createObjectURL should have the following steps added.
Returns a unique Blob URL each time it is called on a valid object argument, which is a non-null Stream in scope of the global object's URL property from which this static method is called. This method must act as follows:
Stream argument that is NOT valid, then user agents must return null.Stream argument,
user agents must run the following sub-steps:
Stream is set, return null.Stream.Blob URI that can be used to dereference the stream argument.| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| object | | ✘ | ✘ |
, nullablerevokeObjectURL, static
The extension onto revokeObjectURL should have the following steps added.
Blob or Stream that is both
valid and in the same origin of the global object’s URL property on which this static method was called,
user agents MUST return a 404 response code when the URL is dereferenced.Blob or Stream that is not valid
or if the value provided for the URL argument is not a Blob URI
or if the URL argument refers to a Blob or Stream that is not in the same origin
as the global object’s URL property, this method call does nothing.
User agents MAY display a message on their error console.
| Parameter | Type | Nullable | Optional | Description |
|---|---|---|---|---|
| url | | ✘ | ✘ |
Streams can be both produced and consumed by various APIs. APIs which create streams are identified as producers, and ones which read and act on a stream are known as consumers. This section identifies some of the APIs where Streams may be produced and consumed.
This section outlines APIs which can consume a Stream object
This section outlines APIs which can produce a Stream object
A Stream should have the same security considerations as a Blob.
This is outlined in 6.8. Security Considerations
of the File API specification. [FILE-API]
Because a Stream uses a Blob URI, cross origin requests on a Stream will not be supported.
This specification proposes an extension to XMLHttpRequest [XMLHTTPREQUEST2] to add support for Stream. This section is temporary and is meant to provide a recommendation for how Stream should be incorporated into XMLHttpRequest.
This will extend XMLHttpRequest to allow for receiving and uploading of a Stream.
One such scenario is providing access to data during readyState 3 (LOADING).
The sections below document in detail what extensions must be done to XMLHttpRequest to support Stream.
The section named "Response Entity Body" in XMLHttpRequest specification [XMLHTTPREQUEST2] should have the following additions:
The stream response entity body is either a Stream representing the response entity body or null.
If the stream response entity body is null, let it be the return value of the following algorithm:
Stream object.
Stream object representing the response entity body.
stream" responseType
A new value for the responseType attribute "stream" should be introduced.
In the IDL list in the section named "Interface XMLHttpRequest" in XMLHttpRequest specification [XMLHTTPREQUEST2], the definition of XMLHttpRequestResponseType enum should now read:
enum XMLHttpRequestResponseType {
"",
"arraybuffer",
"blob",
"stream",
"document",
"json",
"text"
}
response attribute
The algorithm of the response attribute should be modified to handle the new responseType value "stream".
A Stream is binary data obtained sequentially over time.
Given this, a Stream should be accessible in readyState 3 (LOADING).
The section named "The response attribute" in XMLHttpRequest specification [XMLHTTPREQUEST2] should now read:
The response attribute must return the result of running these steps:
responseType is the empty string or "text"responseType is "stream"
The switch in otherwise case of step 4 of
The section named "The send() method"
in XMLHttpRequest specification [XMLHTTPREQUEST2] should have the following additions:
StreamIf the object's type attribute is not the empty string let mime type be its value.
Set the read pending flag for the stream.
Let the request entity body be the raw data represented by data.
Once the read is completed for the request, call close() on the stream
The Stream type allows for completion of several end-to-end experiences. This section covers what the requirements are for this API, and
illustrates some use cases.
XMLHttpRequest in readyState LOADING
Videos can typically be large files that may take a long time to download, and require authentication or certain headers to access. For certain video formats, an application can begin playing the video once the first chunks of data are available, and would not need to wait for the entire video to download.
Stream as it is being read via XMLHttpRequest
If a file format is understood, then an application can make sense of the data as it being made available. For example, a given file may be very large and the application wants to begin processing the data immediately, rather than having to wait for full download of the file.
Stream and XMLHttpRequest
There are situations where an application may have data to upload once the application is processing. This could involve processing of data an application wants to upload as it is being created. One such case is the upload of GPS coordinates within an application. The coordiantes may constantly change, and the application wants to upload the data as it being collected.
Stream and XMLHttpRequest
Media streaming scenarios require the ability to quickly receive data over the network and connect it to a media element. An application can successfully accomplish this by receiving a Stream in readyState LOADING and assign it to a media element. This helps avoid the application from having to buffer the data prior to assigning it to a media element.
Thanks to Eliot Graff for editorial assistance. Special thanks to the W3C. The editor would like to thank Anne van Kesteren, Austin William Wright, Aymeric Vitte, Domenic Denicola, Isaac Schlueter, Jonas Sicking, Kenneth Russell, Yusuke Suzuki, Adrian Bateman for their contributions to this specification.