酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Windowsストアアプリでファイルを特定のディレクトリに保存する(IBuffer型のデータからStorageFile型に変換する)

Windowsストアアプリでファイルを保存したい場合どうしましょう。

エクスプローラーでよく見かける「ライブラリ」に表示されているドキュメントフォルダやピクチャフォルダ、またはアプリケーションのサンドボックス内のローカルフォルダやローミングフォルダへ通常ファイルを保存します。

保存する方法は色々な方法があると思いますが、汎用的に利用できるメソッドを作成してみましょう。Windowsストアアプリは、フォルダはWindows.Storage名前空間のIStorageFolderオブジェクトで表現されています。

前述したフォルダは、固有のプロパティが標準で割り振られています。ドキュメントフォルダはKnownFolders.DocumentsLibrary、ピクチャフォルダはKnownFolders.PicturesLibrary、アプリケーション内のローカルフォルダはApplicationData.Current.LocalFolder等でIStorageFolderオブジェクトを取得することが可能です。

IStorageFolderオブジェクトのCreateFileAsyncメソッドにファイル名を指定して、新しいファイルを作成します。作成したファイルのストリームを開き、保存したいデータのストリームをファイルに書き込むことで動的にファイルを保存することができます。

/// <summary>
/// 指定されたフォルダーへファイルを保存する
/// 既存の同名ファイルが存在している場合はファイルを上書きする
/// </summary>
/// <param name="folder">フォルダー</param>
/// <param name="fileName">拡張子を含むファイル名</param>
/// <param name="stream">保存するデータのストリーム</param>
/// <returns>ファイル</returns>
public static async Task<StorageFile> SaveFileAsync(this IStorageFolder folder, string fileName, IRandomAccessStream stream)
{
    var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
    using (var outputStrm = await file.OpenAsync(FileAccessMode.ReadWrite))
    {
        // 書き込むファイルからデータを読み込む
        var imageBuffer = new byte[stream.Size];
        var ibuffer = imageBuffer.AsBuffer();
        stream.Seek(0);
        await stream.ReadAsync(ibuffer, (uint)stream.Size, InputStreamOptions.None);

        // データをファイルに書き出す
        await outputStrm.WriteAsync(ibuffer);
    }
    return file;
}

SaveToFolderAsyncメソッドを、より扱いやすいように拡張メソッドをSoftbuild.Storage名前空間のStorageExtensionsクラスに定義します。

using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;

namespace Softbuild.Storage
{
    public static class StorageExtensions
    {
        /// <summary>
        /// 指定されたフォルダーへファイルを保存する
        /// 既存の同名ファイルが存在している場合はファイルを上書きする
        /// </summary>
        /// <param name="folder">フォルダー</param>
        /// <param name="fileName">拡張子を含むファイル名</param>
        /// <param name="stream">保存するデータのストリーム</param>
        /// <returns>ファイル</returns>
        public static async Task<StorageFile> SaveFileAsync(this IStorageFolder folder, string fileName, IRandomAccessStream stream)
        {
            var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
            using (var outputStrm = await file.OpenAsync(FileAccessMode.ReadWrite))
            {
                // 書き込むファイルからデータを読み込む
                var imageBuffer = new byte[stream.Size];
                var ibuffer = imageBuffer.AsBuffer();
                stream.Seek(0);
                await stream.ReadAsync(ibuffer, (uint)stream.Size, InputStreamOptions.None);

                // データをファイルに書き出す
                await outputStrm.WriteAsync(ibuffer);
            }
            return file;
        }
    }
}

あとは、ファイルを保存したいソースコードの先頭あたりで、usingディレクティブにSoftbuild.Storage名前空間を追加しておくのを忘れないようにしましょう。IStorageFolderオブジェクトがあればSaveFileAsyncメソッドで保存することができます。

using Softbuild.Storage;

保存先のIStorageFolderオブジェクトとファイル名、ファイルに書き込むデータがあればファイルに保存することができます。

Documentsフォルダへファイルを保存する

Documentsフォルダに書き込みたい場合はこんな感じで。

var folder = KnownFolders.DocumentsLibrary;
await folder.SaveToFolderAsync("alpaca.png", strm);

アプリケーション内のローミングフォルダへファイルを保存する

同一Live IDを使用しているデバイスに同期させたい場合は、ローミングディレクトリに保存します。

var folder = ApplicationData.Current.RoamingFolder;
await folder.SaveToFolderAsync("alpaca.png", strm);

以上で、ファイルの保存の仕方の紹介は終わりです。