Wednesday, 25 February 2015

Streams

Think of a stream as channel from memory to persistent storage and vice versa, it's basically something that's used to write and read date to and from files. the inner mechanism is a bit more involved and something that i don't think i could explain gracefully, so I'll chalk it up to encapsulation, i know it works, i sorta know how it works and if you're really curious i suggest you hunt around for a good explanation:

https://msdn.microsoft.com/en-us/library/k3352a4t(v=vs.110).aspx

basically anything stored in your computer is either a 1 or 0 (quantum computing aside), so whether your data is text, numbers, music, image's videos whatever at the end of the day it's represented by 1's and 0's or binary.

streams just let us stream those 1's and 0's; we can transform them or just read and write them, anyway here are some quick examples, let's start with a UI

<Page
    x:Class="pc.StreamsExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:pc.StreamsExample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height=" 140" />
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
       
        <TextBlock Grid.Row="0" Text="Streams example" Margin="100 0 0 0"
                   VerticalAlignment="Center" Style="{ThemeResource HeaderTextBlockStyle}" />
       
        <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="100 0 0 0">
            <TextBlock Text="Data to Log:" Style="{ThemeResource  SubheaderTextBlockStyle}" />
            <TextBox Margin="10 0 0 0" x:Name="input_TXT" Width="500"/>
            <Button x:Name="log_BTN" Content="Log" />
            <Button x:Name="read_BTN" Content="Read" />
        </StackPanel>
       
        <TextBox x:Name="Log_TXT" Grid.Row="2" Margin="100 20 50 50"
                 Header="Log Content:" FontSize="24"
                 TextWrapping="Wrap" AcceptsReturn="True" />    
    </Grid>

</Page>

and it should look like

next lets look at our code behind and try to log some data:

using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Security.Cryptography;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace pc.StreamsExample
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            this.log_BTN.Click += log_BTN_Click;
        }

        async void log_BTN_Click(object sender, RoutedEventArgs e)
        {
            var lineItem = String.Format("{0}: {1}\r\n", DateTime.Now.ToString("dd-MM-yy hh:mm:ss"), input_TXT.Text);
            IBuffer line = CryptographicBuffer.ConvertStringToBinary(lineItem, BinaryStringEncoding.Utf8);

            //require picture library capability in our app manifest
            var Pictures = KnownFolders.PicturesLibrary;
            var logFile = await Pictures.CreateFileAsync("log.txt", CreationCollisionOption.OpenIfExists);

            await FileIO.WriteBufferAsync(logFile, line);
        }
    }
}


now this will log our file, but what it's going to do is overwrite our file with each log, so that's not very useful, so instead of writing our buffer let's append our file.

swap the following line
    await FileIO.WriteBufferAsync(logFile, line);
With
       await FileIO.AppendTextAsync(logFile, lineItem);

there we go, with that small change we're now appending our file at the end instead of overwriting it. next let's add our read event in our constructor

public MainPage()
{
    this.InitializeComponent();

    this.log_BTN.Click += log_BTN_Click;
    this.read_BTN.Click += read_BTN_Click;

}

and the FileIO code for reading our text document.

async void read_BTN_Click(object sender, RoutedEventArgs e)
{
    StorageFolder Pictures = KnownFolders.PicturesLibrary;
    StorageFile logFile = await Pictures.GetFileAsync("log.txt");

    Log_TXT.Text = await FileIO.ReadTextAsync(logFile);

}

easy as that, now i like the FileIO class for that exact reason it's easy concise and abstracts all the inner workings away from you, making nicer cleaner code to look at, but there's more then one way to skin a cat, so you may very well see something like the following for working with the streams more directly/

async void log_BTN_Click(object sender, RoutedEventArgs e)
{
    var lineItem = String.Format("{0}: {1}\r\n", DateTime.Now.ToString("dd-MM-yy hh:mm:ss"), input_TXT.Text);
    IBuffer line = CryptographicBuffer.ConvertStringToBinary(lineItem, BinaryStringEncoding.Utf8);

    //require picture library capability in our app manifest
    var Pictures = KnownFolders.PicturesLibrary;
    var logFile = await Pictures.CreateFileAsync("log.txt", CreationCollisionOption.OpenIfExists);

    using (var s = await logFile.OpenStreamForWriteAsync())
    {
        s.Position = s.Length;
        s.Write(line.ToArray(), 0, Convert.ToInt32(line.Length));
    }
}

async void read_BTN_Click(object sender, RoutedEventArgs e)
{
    StorageFolder Pictures = KnownFolders.PicturesLibrary;
    StorageFile logFile = await Pictures.GetFileAsync("log.txt");

    using (var stream = await logFile.OpenReadAsync())
    using (var inputStream = stream.GetInputStreamAt(0))
    {
        var dr = new DataReader(inputStream);
        uint numBytesLoaded = await dr.LoadAsync((uint)stream.Size);

        Log_TXT.Text = dr.ReadString((uint)numBytesLoaded);
    }

}

now that's a bit more verbose in my opinion, but hey why not right, and one more variation

async void log_BTN_Click(object sender, RoutedEventArgs e)
{
    var lineItem = String.Format("{0}: {1}\r\n", DateTime.Now.ToString("dd-MM-yy hh:mm:ss"), input_TXT.Text);
    IBuffer line = CryptographicBuffer.ConvertStringToBinary(lineItem, BinaryStringEncoding.Utf8);

    //requrie picture library capablity in our app manifest
    var Pictures = KnownFolders.PicturesLibrary;
    var logFile = await Pictures.CreateFileAsync("log.txt", CreationCollisionOption.OpenIfExists);

    using (var fileStream = await logFile.OpenStreamForWriteAsync())
    using (var outputStream = fileStream.AsOutputStream())
    {
        fileStream.Position = fileStream.Length;
        DataWriter dw = new DataWriter(outputStream);
        dw.WriteBytes(line.ToArray());
        await dw.StoreAsync();
        await outputStream.FlushAsync();
    }
}

async void read_BTN_Click(object sender, RoutedEventArgs e)
{
    StorageFolder Pictures = KnownFolders.PicturesLibrary;
    StorageFile logFile = await Pictures.GetFileAsync("log.txt");

    using (var stream = await logFile.OpenReadAsync())
    using (var inputStream = stream.GetInputStreamAt(0))
    {
        var dr = new DataReader(inputStream);
        uint numBytesLoaded = await dr.LoadAsync((uint)stream.Size);

        Log_TXT.Text = dr.ReadString((uint)numBytesLoaded);
    }

}

there's many different ways to utilize streams, theses just happen to be the approaches I've come across, the thing to know about streams is you pass data into them and they write them read them or transform them, you can pass streams to each other.