Reading and Writing text files in Windows 8 Metro

Perhaps I’m missing something here, but writing a simple text file and reading it back in is surprisingly complex in Windows 8 Metro style apps/WinRT. In .NET there’s a one-liner for such a trivial task, but I haven’t been able to find anything like that in WinRT. So to save you the trouble, here’s my utility methods for this:

Reading a file from the ApplicationData LocalFolder\DataCache:

public static async Task<string> ReadFile(string filename)
{
    var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
    var folder = await localFolder.GetFolderAsync("DataCache");
    var file = await folder.GetFileAsync(filename);
    var fs = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
    var inStream = fs.GetInputStreamAt(0);
    Windows.Storage.Streams.DataReader reader = new Windows.Storage.Streams.DataReader(inStream);
    await reader.LoadAsync((uint)fs.Size);
    string data = reader.ReadString((uint)fs.Size);
    reader.DetachStream();
    return data;
}

Writing to a file in the ApplicationData LocalFolder\DataCache:

public static async void WriteFile(string filename, string contents)
{
    var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
    var folder = await localFolder.CreateFolderAsync("DataCache", Windows.Storage.CreationCollisionOption.OpenIfExists);
    var file = await folder.CreateFileAsync(filename, Windows.Storage.CreationCollisionOption.ReplaceExisting);
    var fs = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
    var outStream = fs.GetOutputStreamAt(0);
    var dataWriter = new Windows.Storage.Streams.DataWriter(outStream);
    dataWriter.WriteString(contents);
    await dataWriter.StoreAsync();
    dataWriter.DetachStream();
    await outStream.FlushAsync();
}

I used this to create a little data cacher , as shown below (Note: you could use the LocalSettings for this, but there’s a very low limit to how large objects stored there can be):

public static void SaveData(string filename, object objectGraph, bool overwriteIfNull = true)
{
    string json = null;
    if(objectGraph != null)
        json = SerializeObjectGraph(objectGraph);
    if (json != null || overwriteIfNull)
    {
        WriteFile(filename, json);
    }
}

private static string SerializeObjectGraph(object graph)
{
    if (graph == null) return null;
    DataContractJsonSerializer ser = new DataContractJsonSerializer(graph.GetType());
    MemoryStream ms = new MemoryStream();
    ser.WriteObject(ms, graph);
    var bytes = ms.ToArray();
    return UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}

public static async Task<T> LoadData<T>(string filename)
{
    var json = await ReadFile(filename);
    MemoryStream ms = new MemoryStream(UTF8Encoding.UTF8.GetBytes(json));
    DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
    T result = (T)ser.ReadObject(ms);
    return result;
}

If there’s an easier way to do this, please do let me know. If not, hopefully we’ll get some more utility/helper methods as the SDK matures.

UPDATE: Turns out that there is an easier way to do this after all using the Windows.Storage.PathIO class!

Reading a text file in one line of code using WinRT:

string contents = await Windows.Storage.PathIO.ReadTextAsync("ms-appdata:///local/DataCache/test.txt");

Opening a managed stream using a path:

using (var stream = await Windows.Storage.ApplicationData.Current.LocalFolder.OpenStreamForReadAsync("DataCache\\test.txt"))
    using (var streamReader = new StreamReader(stream))
        contents = streamReader.ReadToEnd();

Similarly Windows.Storage.PathIO also have other neat one-liners. Check out the reference here: http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.pathio.aspx

Notice that there is also a WriteTextAsync(string,string) method. However this seems to throw an exception if the file doesn’t already exist, so I’m not sure how useful that is at this point, considering this will be a-chicken-and-the-egg scenario. So for now, the code above is still useful for writing files, and of course also for understanding the more low-level file operations.

Comments (7) -

  • I think your WriteFile should return a Task...
  • Filip: You could do that. It's not required (I designed the call as a fire and forget), but if you want to wait for the write to complete, change 'void' to 'task' and you're good to go)
  • Geez, thanks for this. I was struggling to use the new storage namespace to get a file out of localFolders. Appreciate it Laughing
  • Ummm... seriously?  I guess I'll be moving to use this framework about the time that h*ll freezes over.  Talk about taking a giant leap backwards in developer productivity and code maintainability.
  • Well, this new framework is coming from the Windows Division, not the Developer Division - no wonder the focus is on Windows and not developers! Laughing
  • Filip: What makes you think that? This is from the dev devision. And I would hope an API had focus on the platform it belongs to.
    I think it's awesome how file operations are now forced async - this helps us a lot when storage becomes slow operations (think cloud storage for instance). Of course it adds a complexity like awaiting folder creation etc. However it quickly becomes boilerplate code - code I just wrote for you and you can simply reuse. I expect as the framework evolves we will have more utility methods provided out of the box for the day-to-day tasks (but they will never replace all the individual calls shown above and the fine grained power they provide).
  • My big problem with this new forced asynch direction is that it treats me like a child who can't design proper software.  In .NET I can decide if I need to make something asynch and I can make the decision based on my knowledge of the problem.  WinRT's asynch approach is like taking french fries off the market because people can't control their eating.  If Microsoft really wanted to do something to help they could have fixed the fact that there is a single UI thread.

Pingbacks and trackbacks (5)+

Add comment