Question #2: My God, what are all those different stream types about?
Answer: Trust me, I feel your pain. That’s why I’m writing this post! There are a bunch of classes and interfaces for streams, so let’s sort them out.
A stream is just an abstraction for a bit bucket. Streams don’t make any distinction about the data in those buckets, only about how you can get to those bits. Streams are used to access file contents, pass information over sockets, talk to devices, and so forth.
Streams come in two basic flavors: original and jalapeño. Oops! I’m writing this while cooking dinner…sorry about that. They come in two sorts: sequential and random access. This differentiation allows for certain optimizations to be made in the implementation of the stream.
- A sequential stream can assume that data is accessed (read or written) once, after which it no longer needs to be cached in memory. Sequential streams do not support seeking or positioning. When possible, it’s more memory-efficient to use a sequential stream.
- A random access stream needs to keep that data around in case the consumer wants to rewind or fast-forward (seek or position).
In the sequential group there is a further distinction between “input” streams, which support reading (but not writing), and “output” streams that support writing (but not reading). The primary classes in this group are FileInputStream and FileOutputStream; there are also the IInputStream and IOutputStream interfaces that serve as the basic abstractions. (Don’t concern yourself with InputStreamOverStream and OutputStreamOverStream, which are wrappers for lower-level COM IStream objects.)
An input stream has a method readAsync that copies bytes from the source into something called a buffer, which we’ll talk about later on (a buffer is an abstraction for a byte array). An output stream has two methods, writeAsync, which copies bytes from a buffer to the stream, and flushAsync which makes sure the data is written to the target before it deems the flushing operation is complete.
Now for the random access group first. Within Windows.Storage.Streams we find the basic types: FileRandomAccessStream, InMemoryRandomAccessStream, and RandomAccessStream, alongwith the abstract interface IRandomAccessStream. (RandomAccessStreamOverStream again builds on the COM IStream but isn’t something you use directly).
The methods of IRandomAccessStream are common among classes in this group. It provides properties of canRead, canWrite, position, and size, along with methods seek, cloneStream (the clone has an independent position and lifetime), getInputStreamAt (returns an IInputStream), and getOutputStreamAt (returns an IOutputStream). With these last two you can see that they provide a sequential stream for some section of the random access stream, allowing more efficient read/write operations. You often use these methods to obtain the right kind of sequential stream to pass to come other API that requires them.
If we now look at FileRandomAccessStream and InMemoryRandomAccessStream (whose backing data sources I trust are obvious), they have everything we’ve seen already (properties like position and canRead, and methods like close and seek) along with two more methods, readAsync and writeAsync that behave just like their counterparts in sequential input and output streams (using those buffers again).
As for the RandomAccessStream class, it’s a curious beast that contains only static members–you never have an instance of this one. It exists to provide the generic helper methods copyAsync (with two variants) and copyAndCloseAsync that transfer data between input and output streams. This way other classes like FileRandomAccessStream don’t need their own copy methods. So to copy content from one file into another, you call FileRandomAccessStream.getInputStreamAt on the source (for reading) and FileRandomAccessStream.getOutputStreamAt on the destination (for writing), then pass those to RandomAccessStream.copyAsync (to leave those streams open) or copyAndCloseAsync (to automatically do a flushAsync on the destination and close on both)
The other class to talk about here is another one that supplies other static members: RandomAccessStreamReference. When you read “reference,” avoid thinking about reference types or pointers or anything like that–it’s more about having read-only access to resources that might not be writable, like something a URI points to.
The three static methods are createFromFile (which takes a StorageFile), createFromStream (which takes an IRandomAccessStream), and createFromUri (which takes a Windows.Foundation.Uri that you construct with a string). What you then get back is an instance of RandomAccessStreamReference (how’s that for confusing?), which then has one method, openReadAsync whose result is basically an IRandomAccessStream with an extra string property contentType (technically coming from IRandomAccessStreamWithCntentType that inherits the property from IContentType). This simply tells you about the data format therein.
To sum up, then, remember the difference between sequential streams (input or output) and random access streams, and that streams are just ways to talk to the bits (or bytes) that exist in some backing entity like a file or memory. When a stream exists, its backing entity is “open” until the stream is closed or disposed of.