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.