User Tools

Site Tools


notes:uwp:appdata

Files and Streams in UWP

The ApplicationData class represents the Application Local Storage area on the hard drive that is private to an app. It contains folders where the app can store data. These folders (local, roaming and temporary) are created when the app is installed.

Obtain access to the local, roaming, and temporary folders:

using Windows.Storage;
...
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFolder roamingFolder = ApplicationData.Current.RoamingFolder;
StorageFolder temporaryFolder = ApplicationData.Current.TemporaryFolder;

Obtain the path to the local folder:

string path = ApplicationData.Current.LocalFolder.Path;

FileIO:

  • AppendLinesAsync
  • AppendTextAsync
  • ReadLinesAsync
  • ReadTextAsync
  • ReadBufferAsync
  • WriteLinesAsync
  • WriteTextAsync
  • WriteBufferAsync
  • WriteBytesAsync

IBuffer:

  • Represents an array of bytes in system memory.
  • Provides a convenient way to transfer bytes from one stream to another.
  • It is possible to convert IBuffer to an array of bytes.
  • Defines two properties: Capacity and Length.
  • The ReadAsync method defined by the IInputStream interface reads from a stream into an IBuffer.
  • The WriteAsync method defined by the IOutputStream interface writes from the IBuffer into the stream.

FileIO, FolderIO, PathIO:

  • Static methods FileIO, FolderIO, and PathIO are not suitable for working with large files because they read the whole file into memory all at once. For working with large files use streams.

DataReader and DataWriter:

  • The Windows.Storage.Streams.DataWriter and Windows.Storage.Streams.DataReader classes are equivalent of the .NET System.IO.BinaryWriter and System.IO.BinaryReader classes for storing and retrieving primitive data types from a stream.

Creating Files

using Windows.Storage;
...
var localFolder = ApplicationData.Current.LocalFolder;
 
// Create a new file or open an existing one.
StorageFile file = await localFolder.CreateFileAsync("data.txt", CreationCollisionOption.OpenIfExists);
 
// Create a new file or replace an existing one.
StorageFile file = await localFolder.CreateFileAsync("data.txt", CreationCollisionOption.ReplaceExisting);
 
// Create a new file with a unique name.
StorageFile file = await localFolder.CreateFileAsync("data.txt", CreationCollisionOption.GenerateUniqueName); 
 
// Obtain a file by its name.
StorageFile file = await localFolder.GetFileAsync("data.txt");
 
// Obtain a file by its full path.
string path = System.IO.Path.Combine(localFolder.Path, "data.txt");
StorageFile file = await StorageFile.GetFileFromPathAsync(path);
 
// Obtain all files located in a given folder.
IReadOnlyList<StorageFile> files = await localFolder.GetFilesAsync()

You can also access files using the StorageFile.GetFileFromApplicationUriAsync method and providing a URI with the ms-appdata:/// prefix.

Folder URI Prefix
LocalFolder ms-appdata:///local
RoamingFolder ms-appdata:///roaming
TemporaryFolder ms-appdata:///temp
InstallFolder ms-appx:///
// Retrieve a file test.txt located in ApplicationData.Current.LocalFolder.
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appdata:///local/test.txt"));

ms-appx-web points to resources stored in the app's package for use in web scenarios. It accesses the same files as ms-appx but in the web compartment.

To programmatically access a file located in the install directory (the package's directory) set the file's Build Action property to Content. Below, there are two examples of accessing the file located in the install directory:

using Windows.Storage;
...
// Method #1
StorageFolder folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
StorageFile file = await folder.GetFileAsync(@"Assets\Image.png");
 
// Method #2
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/image.png"));

Note that ms-appx:///Assets/image.png is equivalent to

"ms-appx://" + Windows.ApplicationModel.Package.Current.Id.Name + "/Assets/image.png"

Writing to Files

Write text to a file:

using Windows.Storage;
...
// Write a pice of text.
await FileIO.WriteTextAsync(file, "some text");
 
// Write a couple of lines of text.
String[] output = new[] { "Line 1", "Line 2" }; 
await FileIO.WriteLinesAsync(file.Path, output); 

Append text to a file:

using Windows.Storage;
using Windows.Storage.Streams; // for UnicodeEncoding.Utf8
...
// The file has been created with CreateFileAsync(filename, CreationCollisionOption.OpenIfExists).
await FileIO.AppendTextAsync(file, "some text", UnicodeEncoding.Utf8);

Write bytes to a file using a buffer:

using Windows.Security.Cryptography;
using Windows.Storage;
...
// Get a buffer of bytes. Here, we get it from a string.
var buffer = CryptographicBuffer.ConvertStringToBinary("text data", BinaryStringEncoding.Utf8);
 
// Write the bytes from the buffer to a StorageFile.
await FileIO.WriteBufferAsync(file, buffer);
using Windows.Storage;
using Windows.Storage.Streams; // for DataWriter
...
using (DataWriter writer = new DataWriter())
{
    // Write binary data to DataWriter.
    // DataWriter stores data in an internal IBuffer.
    writer.WriteBytes(new byte[] { 120, 34, 18, 234, 68 });
 
    // Write data from the internal IBuffer to a StorageFile.
    await FileIO.WriteBufferAsync(file, writer.DetachBuffer());
}
using Windows.Storage;
using Windows.Storage.Streams; // for DataWriter
...
await FileIO.WriteBufferAsync(file, GetBuffer());
...
// A helper method to prepare a buffer containing binary data.
private IBuffer GetBuffer()
{
    IBuffer buffer;
 
    using (DataWriter writer = new DataWriter())
    {
        writer.WriteBytes(new byte[] { 120, 34, 18, 234, 68 });
        buffer = writer.DetachBuffer();
    }
 
    return buffer;
}

Write variety of data (bytes, ints, text) to a file:

using Windows.Storage;
using Windows.Storage.Streams; // for DataWriter
...
using (var writer = new DataWriter(await file.OpenAsync(FileAccessMode.ReadWrite)))
{
    writer.WriteBytes(new Byte[] { 8, 6, 4 });
 
    const string str = "Testing";
 
    // MeasureString returns how many bytes a string requires when encoded via UnicodeEncoding.
    uint len = writer.MeasureString(str); // measure the string taking into account encoding (len==7)
    writer.WriteUInt32(len); // store the string length
    writer.WriteString(str); // store the string
 
    uint bytesStored = await writer.StoreAsync(); // commit buffer to the stream
    Debug.WriteLine($"Bytes stored: {bytesStored}"); // 14
} // close DataWriter and the underlying stream

Write text to a file using a stream:

using Windows.Storage;
using Windows.Storage.Streams;
...
// Open a StorageFile and return a stream of the file's content when the OpenAsync operation completes.
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
    // Get an output stream. Specify 0 to set the position to the beginning of the stream.
    using (var outputStream = stream.GetOutputStreamAt(0))
    {
        using (var writer = new DataWriter(outputStream))
        {
            // Write some text to the output stream.
            writer.WriteString("This is a string.");
 
            // Save the text to the file.
            await dataWriter.StoreAsync();
 
            // Flush and close the stream.
            await outputStream.FlushAsync();
        }
    }
}

Reading from Files

Read text from a file:

using Windows.Storage;
...
// Read the entire StorageFile as a single string.
string text = await FileIO.ReadTextAsync(file);
...
// Read the entire StorageFile as a list of strings. Each string should be separated by a NewLine character.
// The strings are decoded with UTF-8.
IList<string> list = await FileIO.ReadLinesAsync(file);

Read text from a file using a buffer:

using Windows.Storage;
using Windows.Storage.Streams; // for DataReader
...
IBuffer buffer = await FileIO.ReadBufferAsync(file);
 
// Read the buffer contents.
using (var reader = DataReader.FromBuffer(buffer))
{
    string text = reader.ReadString(buffer.Length);
}

Read binary data from a file using a buffer:

using Windows.Storage;
using Windows.Storage.Streams; // for DataReader
...
IBuffer buffer = await FileIO.ReadBufferAsync(file);
 
using (var reader = DataReader.FromBuffer(buffer))
{
    byte[] data = new byte[buffer.Length];
    reader.ReadBytes(data);
}

Read text from a file using a stream:

using Windows.Storage;
using Windows.Storage.Streams; // for DataReader
...
// Open a StorageFile and return a stream of the file's content when the OpenAsync operation completes.
var stream = await file.OpenAsync(FileAccessMode.Read);
 
// Get the size of the stream.
ulong size = stream.Size;
 
// Get an output stream. Specify 0 to set the position to the beginning of the stream.
using (var inputStream = stream.GetInputStreamAt(0))
{
    // Read the text.
    using (var reader = new DataReader(inputStream))
    {
       uint bytesLoadedCount = await reader.LoadAsync((uint)size);
       string text = reader.ReadString(bytesLoadedCount);
    }  
}

Read variety of data (bytes, ints, text) from a file:

using Windows.Storage;
using Windows.Storage.Streams; // for DataReader
...
using (var reader = new DataReader(await file.OpenAsync(FileAccessMode.Read)))
{
    // LoadAsync(count) reads count bytes from a stream appending them to a buffer.
    byte[] bytes = new byte[3];
    uint bytesRead = await reader.LoadAsync((uint)bytes.Length);
    reader.ReadBytes(bytes);
 
    // Read an integer. In this example, this is the length of the string that comes next in the file.
    bytesRead = await reader.LoadAsync(sizeof(uint));
    var n = reader.ReadUInt32();
 
    // Read a string of the length n.
    bytesRead = await reader.LoadAsync(n);
    string str = reader.ReadString(n);
    Debug.WriteLine($"String: {str}");
} // close DataReader and the underlying stream
using Windows.Storage;
using Windows.Storage.Streams; // for DataReader
...
// Open the file for reading. 
// StorageFile.OpenReadAsync returns IAsyncOperation<IRandomAccessStreamWithContentType>.
using (IRandomAccessStream stream = await file.OpenReadAsync())
{
    // Attach the stream to a DataReader for reading.
    using (DataReader reader = new DataReader(stream))
    {
        uint num = (uint)stream.Size;
 
        // Load the number of bytes 'num' from a disk to a buffer (of type IBuffer).
        await reader.LoadAsync(num);
 
        // Read the bytes from the buffer as a string.
        string str = reader.ReadString(num);
    }
}

Read binary data from a file using a .NET class System.IO.BinaryReader and the extension method AsStreamForRead:

using System.IO; // for BinaryReader
using Windows.Storage;
using Windows.Storage.Streams;
...
// The OpenReadAsync method opens a random-access stream over the current file for reading file contents.
// When the method completes, it returns the random-access stream of type IRandomAccessStreamWithContentType.
using (IRandomAccessStreamWithContentType stream = await file.OpenReadAsync())
{
    stream.Seek(0);
 
    using (BinaryReader reader = new BinaryReader(stream.AsStreamForRead()))
    {
        byte[] buffer = new byte[stream.Size];
        reader.Read(buffer, 0, buffer.Length);
    }
}

You can also read data using PathIO.ReadBufferAsync:

// Read data from a location provided as an URI. PathIO.ReadBufferAsync also accepts an absolute path.
var buffer = await PathIO.ReadBufferAsync(uri);

Copying Files

Copy a file from the installation folder to the local folder:

// Method #1
 
// sourcePath is the path of a file in the installation folder. It must be prefixed with ms-appx:/// 
string sourcePath = "ms-appx:///data.txt";
 
// destFilename is the desired name of the filename in the local folder.
string destFilename = "data.txt";
 
// Obtain a file from the installation folder.
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(sourcePath));
 
// Copy the file to the local folder.
await file.CopyAsync(Windows.Storage.ApplicationData.Current.LocalFolder, 
    destFilename, NameCollisionOption.ReplaceExisting);
// Method #2
using Windows.ApplicationModel;
using Windows.Storage;
...
StorageFile sourceFile = await Package.Current.InstalledLocation.GetFileAsync("Assets\\logo.png");
StorageFile destFile = 
  await ApplicationData.Current.LocalFolder.CreateFileAsync("copy.png", CreationCollisionOption.ReplaceExisting);
 
// Create a new file.
var buffer = await FileIO.ReadBufferAsync(sourceFile);
await FileIO.WriteBufferAsync(destFile, buffer);

Deleting Files

Delete all package files:

await ApplicationData.Current.ClearAsync();

KnownFolders

Known Folder Description
MusicLibrary Gets the Music library
PicturesLibrary Gets the Pictures library
VideosLibrary Gets the Videos library
HomeGroup Gets the HomeGroup folder. The app must be configured with the Music Library, Pictures Library, or Videos Library capability in the package manifest.
RemovableDevices Gets the Removable Devices section in My Computer. Includes devices such as USB flash drives and external portable drives. The app must be configured with the Removable Storage capability.
MediaServerDevices Gets the Media Server Devices (Digital Living Network Alliance [DLNA]) folder. The app must be configured with the Music Library, Pictures Library, or Videos Library capability in the package manifest.
DocumentsLibrary The use of the DocumentsLibrary capability is discouraged in Win 8.1 and Win 10

Example: Access items in PicturesLibrary:

StorageFolder picturesFolder = KnownFolders.PicturesLibrary;
IReadOnlyList<IStorageItem> items = await picturesFolder.GetItemsAsync();
 
foreach (IStorageItem item in items)
{
    if (item is StorageFolder)
        Debug.WriteLine("Folder: " + item.Name);
    else if (item is StorageFile)
        Debug.WriteLine("File: " + item.Name);
    else
        Debug.WriteLine("Other: " + item.Name);
}

Example: Group pictures by their creation month using the CreateFolderQuery method. The CreateFolderQuery method filters all files in the current folder as well as in all subfolders. That's why some files are listed twice when multiple copies are located in different subfolders.

using Windows.Storage.Search;
...
StorageFolder library = KnownFolders.PicturesLibrary;
 
if (library.IsCommonFolderQuerySupported(CommonFolderQuery.GroupByMonth))
{
    StorageFolderQueryResult result = library.CreateFolderQuery(CommonFolderQuery.GroupByMonth);
    IReadOnlyList<IStorageItem> folders = await result.GetFoldersAsync();
    foreach (StorageFolder folder in folders)
    {
        IReadOnlyList<StorageFile> files = await folder.GetFilesAsync();
 
        // Show the month and the number of files in this group.
        Debug.WriteLine("{0} ({1})", folder.Name, files.Count);
 
        // Show the filenames.
        foreach (StorageFile file in files)
            Debug.WriteLine("    {0}", file.Name);
    }
}

Output:

November 2014 (3)
    list.txt
    test.txt
    winrt_06.png
October 2014 (1)
    winrt_05.png
September 2014 (3)
    Logo24.psd
    Logo30.psd
    Logo42.psd
February 2013 (1)
    image1.jpg
January 2013 (4)
    pic1.jpg
    pic1.jpg
    pic2.jpg
    pic2.jpg
July 2006 (2)
    Lighthouse.jpg
    Lighthouse.jpg

Indexed Folder

Windows Search does not index the contents of your package's folders (more details here). However, if you create a folder called “Indexed” in your package's local folder, Windows Search will index the contents of this folder.

Example: Create the “Indexed” folder, add two text files to it, and then perform a query against the folder:

// Create the "Indexed" subdirectory in the Local package folder. 
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFolder indexed = await localFolder.CreateFolderAsync("Indexed");
 
// Create two text files in the "Indexed" subdirectory: 
await FileIO.WriteTextAsync(await indexed.CreateFileAsync("data1.txt"), "aa bb cc");
await FileIO.WriteTextAsync(await indexed.CreateFileAsync("data2.txt"), "aa dd ee");
 
// Create a query that looks for .txt files containing "dd" 
var query = new QueryOptions(CommonFileQuery.DefaultQuery, new[] { ".txt" })
{
    ApplicationSearchFilter = "dd",
    IndexerOption = IndexerOption.OnlyUseIndexer, // use indexed files only 
    FolderDepth = FolderDepth.Deep // search subdirectories too 
};
 
StorageFileQueryResult results = localFolder.CreateFileQueryWithOptions(query);
 
// Perform the query and get the results.
IReadOnlyList<StorageFile> result = await results.GetFilesAsync(); 
 
// 'result' is a list containing a single StorageFile - data2.txt

Semaphore

Example: Write a piece of text to a file in a multithreaded environemnt:

private SemaphoreSlim mutex = new SemaphoreSlim(1);
public async void WriteLine(string str)
{
    await mutex.WaitAsync();
    try
    {
        // Create a new file or return an existing file if it already exists.
        var folder = ApplicationData.Current.LocalFolder;
        StorageFile file = await folder.CreateFileAsync("data.txt", CreationCollisionOption.OpenIfExists);
 
        await FileIO.AppendTextAsync(file, str);
    }
    finally
    {
        mutex.Release();
    }
}

More info on that here.

Shared Storage

Shared storage allows your app to communicate with other apps by exposing a file such that another app can operate on it. Access to the file is not directly granted; rather a token is generated that can then be shared between participating apps and used to access the file.

1. Add the file to the shared storage using the SharedStorageAccessManager class and return a token that represents the permission to access that file.

using Windows.ApplicationModel.DataTransfer;
...
// Create or replace the file in the local storage.
var folder = ApplicationData.Current.LocalFolder;
var file = await folder.CreateFileAsync("test.txt", CreationCollisionOption.ReplaceExisting);
await FileIO.AppendTextAsync(file, "Testing...", Windows.Storage.Streams.UnicodeEncoding.Utf8);
 
// Add the file to the shared storage and obtain a token.
token = SharedStorageAccessManager.AddFile(file);

2. From the other app, use the token to retrieve the file using a call to RedeemTokenForFileAsync:

using Windows.ApplicationModel.DataTransfer;
...
var file = await SharedStorageAccessManager.RedeemTokenForFileAsync(token);
if (file != null)
{
    string data = await FileIO.ReadTextAsync(file);
    Debug.WriteLine(data); // Testing...
}

ApplicationData Versioning

The ApplicationData class provides members to version the package data if you want to change the schema of the data.

public sealed class ApplicationData 
{ 
   // Members related to versioning the package's data. 
   // Typically used when the user upgrades to a new version of the package.    
   public UInt32 Version { get; }    
   public IAsyncAction SetVersionAsync( UInt32 desiredVersion, ApplicationDataSetVersionHandler handler);     
 
   // Members that clear out all (or some) of the package's per-user data    
   public IAsyncAction ClearAsync(); 
   public IAsyncAction ClearAsync(ApplicationDataLocality locality); 
 
   ...
}
// Query your packages data version number.
var appDataVersion = ApplicationData.Current.Version;
// Calling SetVersionAsync synchronously.
const UInt32 appDataLatestVersion = 2; 
if (ApplicationData.Current.Version < appDataLatestVersion) 
{
    // ApplicationData.Current.SetVersionAsync returns IAsyncAction
    ApplicationData.Current.SetVersionAsync(
        appDataLatestVersion,
        AppDataSetVersion).AsTask().GetAwaiter().GetResult();
}

The system associates a version number (a 32-bit unsigned integer) with it. By default, your package's data is assigned a version 0.

notes/uwp/appdata.txt · Last modified: 2018/04/19 by leszek