Windows PhoneからSkyDriveにファイルをアップロードする方法

既にMarketplaceから公開されているSensor Checkerを見ながら思った。計測結果をファイル化したいと。都合よくLive SDK v5.0が公開され思った。SkyDriveに計測結果をアップしたら便利だよなと、無料で25GBだし…ということで、計測結果をファイル化してSkyDriveにアップする機能拡張を行いました。既に機能追加版はMarketplaceにアップロードしたので、Rejectされなければ直ぐアップデートされるはずです。

SkyDriveへのファイルアップロードは、意外と簡単。方法を順番に説明していきます。

1. 最新版のLive SDKをダウンロード&インストール

https://www.microsoft.com/download/en/confirmation.aspx?id=28195 からファイルをダウンロードして、ダブルクリック&インストールします。

2. Windows Phone向けのアプリケーションプロジェクトを作成し、参照設定にLiveのコンポーネントを追加

  • Microsoft.Live
  • Microsoft.Live.Controls

を追加します。

3. Appクラスに、LiveConnectSession型のスタティックなプロパティを追加する

Appクラスは、App.xaml.csにあります。

using Microsoft.Live;

を先頭に加え、

    public LiveConnectSession LiveSession { get; set; }

を定義しておきます。

これで準備は万端です。

4. Windows Liveへのサイン用ページを作成

プロジェクトにページを一枚追加します。そのページの先頭のほうに、名前空間を以下の様に追加します。

<phone:PhoneApplicationPage 
    …
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:live="clr-namespace:Microsoft.Live.Controls;assembly=Microsoft.Live.Controls"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    …

そして、Windows Liveのサインインページにナビゲートする為のボタンを追加します。

            <StackPanel Name="SigninPanel" Grid.Row="0">
                <TextBlock Text="Signin to SkyDrive" FontSize="24" HorizontalAlignment="Center"/>
                <live:SignInButton Name="buttonSignin" Content="Sign-in to SkyDrive"
                                   ClientId="########"                                                                                             ← クライアントID
                                   Scopes="wl.basic wl.skydrive wl.offline_access wl.signin wl.skydrive_update"    ←許可範囲
                                   RedirectUri="https://oauth.live.com/desktop"
                                   Branding="Skydrive"
                                   SignInText="SignIn"
                                   SessionChanged="buttonSignin_SessionChanged"                                             ←セッション情報変化の通知を受ける為のハンドラ登録
                                   />
            </StackPanel>

LiveのWindows Phone用部品としてサインイン用ボタンが用意されています。これを使えば簡単にSkyDriveにアクセスする為の認証が行えます。
アトリビュートのClientIdは、https://manage.dev.live.com/ にアクセスして作成するアプリを登録すると、そこで表示されるので、その値をコピペします。
Scopesは、サインインする際にアプリケーションに対して、アクセス要求の範囲を指定します。サインインの際、ユーザーに対して、これらのアクセスを許可するかの判断が任されます。
詳しくは、https://msdn.microsoft.com/en-us/library/hh243646.aspx を見てください。ファイルのアップロードを行うには、wl.skydrive_updateを指定する必要があります。

実行すると以下のページが表示されます。

サインインをタップすると、以下の画面が表示され、Live IDとパスワードを入力し、サインインをタップすると、Scopesに対応した許可画面が表示されます。

 →

右側の図で表示される項目はScopesの指定によって変わりますが、ここでユーザーが”はい”をタップすると、SkyDriveへのアクセスが可能になります。
SessionChangedアトリビュートに登録したハンドラは、

        private void buttonSignin_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
        {
            if (e.Session != null && e.Status == LiveConnectSessionStatus.Connected)
            {
                App.LiveSession = e.Session;
                …
            }
            else
            {
                MessageBox.Show("Signin Failed. ");
            }
        }

こんな感じで、接続が成功したら、AppクラスのLiveSessionプロパティにセッションを保存しておきます。

5. SkyDriveのフォルダー情報取得

ファイルをアップロードする場合、アップロード先のフォルダー情報が必要です。フォルダー情報を取得するには、以下の様なコードを書きます。

            LiveConnectClient client = new LiveConnectClient(App.LiveSession);
            client.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(client_GetCompleted);
            client.GetAsync("me/skydrive/files?filter=folders", client);

フォルダー情報を取得するには、4.で取得したSession情報を使って先ずLiveConnectClientのインスタンスを作成します。clientのGetAsync()メソッドを、赤字で書いた文字列を引数にしてコールすると、応答が帰ってくると、GetCompletedに登録したハンドラがコールされます。
赤字の部分を解読すると、LiveのAPIはREST形式のAPIなので、URLが"me/skydrive/files"で、引数が"filter=folders"で、検索条件としてfoldersを指定してクエリーをかけてると読めます。GetAsyncの2番目の引数は、GetCompletedがコールされた際に再利用する為の状態変数で、ここではclientを渡しています。

GetCompletedのハンドラは、以下の様なコードです。

        void client_GetCompleted(object sender, LiveOperationCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                try
                {
                    string path = null;
                    Dictionary<string, object> folderData = e.Result as Dictionary<string, object>;
                    List<object> folders = (List<object>)folderData["data"];
                    foreach (var item in folders)
                    {
                        Dictionary<string, object> folder = item as Dictionary<string, object>;
                        if (folder["name"].ToString() == uploadFolderName)
                        {
                            path = folder["id"].ToString();
                            break;
                        }
                    }
                    if (path != null)
                    {
                        UploadFile(path, e.UserState as LiveConnectClient);
                    }
                    else
                    {
                        …
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }

このハンドラに渡される引数eのResultは、文字列をキーとし、object型の変数を値とする結果が渡されてきます。実際どんな値が送られてくるかは、デバッグ時ステップ実行して確かめてください。基本は、"data”という文字列をキーにした値には、SkyDriveのドキュメント直下に存在するフォルダー情報を格納したリストが入っています。そのリストにはまたキーバリューペアが入っているのでDictionary型で受けます。"name"というキーにはフォルダー名が格納されていて、"id"にそのフォルダーのパス(path) が格納されています。フォルダーに対して取得できる情報については、https://msdn.microsoft.com/en-us/library/hh243648.aspx#folder を参考にしてください。

このコードでアップロード先のフォルダーのパスを取得して、ファイルをアップロードするわけです。次にUploadFileの中身を紹介します。

6. ファイルのアップロード

            var storage = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
            {
                fstream = storage.OpenFile(filename, System.IO.FileMode.Open);
                client.UploadCompleted += new EventHandler<LiveOperationCompletedEventArgs>(client_UploadCompleted);
                client.UploadAsync(path, filename, fstream);
            }
clientは、LiveConnectClientのインスタンス、pathは、5.で説明した、フォルダーのSkyDrive上のパス(id)です。filenameは、アップロードしたいファイル名で、このコードは、IsolatedStorageに格納されているファイルを同じファイル名でアップロードする事を前提としてコーディングされています。UploadAsync()メソッドをコールすると、実際にSkyDriveにアップロードが開始されるわけです。このメソッドの3番目の引数がStreamになっているので、カメラや音源など、予め作成しておいたファイルでなくてもアップロードが可能と思われます。

実際にやってみたところ、ファイル名の拡張子がxml、つまりXMLファイルの場合は、上手くアップロードできないようです。私はこれで半日嵌りましたので、皆さんご注意。

ちょっと長くなりましたが、SkyDriveへのファイルアップロードは完全なパターンコードです。SkyDriveはLive IDを登録するだけで、無料で25GBのストレージが使えます。アクセス権の設定なども可能なので、いろんな種類のファイルをアプリからアップロードしたい方、是非お試しを。