MRが楽しい

MRやVRについて学習したことを書き残す

HoloLensのRestAPIをコンソールアプリ(.NET Framework)で実行する その3(ファイルのアップロード)

本日は HoloLens の調査枠です。
前回記事の続きです。
bluebirdofoz.hatenablog.com

今回はPOSTメッセージを使って HoloLens の特定のディレクトリにファイルをアップロードしてみます。
f:id:bluebirdofoz:20190304093447j:plain

なお、HoloLens で利用可能な API は以下の公式ページにまとめられています。
・デバイス ポータル コア API リファレンス(すべての Windows 10 デバイスに共通する API)
docs.microsoft.com
・デバイス ポータル Mixed Reality API リファレンス(HoloLens で利用できるすべての REST API の拡張リスト)
docs.microsoft.com

サンプルコード

HoloLens の特定のディレクトリにファイルをアップロードするサンプルコードを作成します。
ファイルアクセスの API は以下の /api/filesystem/apps/file です。
f:id:bluebirdofoz:20190304093458j:plain

ファイルをアップロードする場合、POSTメッセージを利用します。
ただし、ブラウザを介さないWifi接続を利用する場合はSSLを無効にしないとPOSTが失敗するようです。
DevicePortalから Preferences -> SSLconnection -> required のチェックを外しておきます。
f:id:bluebirdofoz:20190304093509j:plain

ローカルフォルダにある upload.jpg を、HoloLens の Picuture フォルダにアップロードするサンプルコードを作成してみました。

以下の通り、Program.cs を修正しました。
・Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// HttpClient を利用するための追加
using System.Net.Http;

namespace HoloLensAPIConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            // 実行例 "HoloLensAPIConsole.exe D:\WORK\HoloLensAPITest\Upload\UploadImage.jpg"
            // アップロードするファイルパスは引数で指定する
            if (args.Length == 1)
            {
                // 1つ目の引数をファイルパスとして取得する
                string uploadFilePath = args[0];

                // ファイルの取得処理を呼び出す
                HttpHoloAccess access = new HttpHoloAccess();
                access.PostFile(uploadFilePath);
            }

            return;
        }

        /// <summary>
        /// HoloLensのRestAPIへのアクセスクラス
        /// </summary>
        class HttpHoloAccess
        {
            // HoloLensの接続設定
            // IPアドレス、ユーザー名、パスワード
            string Target_ipaddress { get; }
            string Target_username { get; }
            string Target_password { get; }

            /// <summary>
            /// インスタンス生成(指定無し)
            /// </summary>
            public HttpHoloAccess()
            {
                // 指定がなければデフォルト設定を利用する
                Target_ipaddress = "127.0.0.1:10080";
                Target_username = "USERNAME";
                Target_password = "PASSWORD";
            }

            /// <summary>
            /// インスタンス生成(接続先設定あり)
            /// </summary>
            /// <param name="a_ipaddress">HoloLesnのIPアドレス</param>
            /// <param name="a_username">DevicePortalのユーザー名</param>
            /// <param name="a_password">DevicePortalのパスワード</param>
            public HttpHoloAccess(string a_ipaddress, string a_username, string a_password)
            {
                // 接続先の指定があれば設定を行う
                Target_ipaddress = a_ipaddress;
                Target_username = a_username;
                Target_password = a_password;
            }

            /// <summary>
            /// ファイルのアップロード関数
            /// (Picturesフォルダ直下のファイルをアップロードする)
            /// </summary>
            /// <param name="a_UploadFilePath">アップロードファイルパス</param>
            public void PostFile(string a_UploadFilePath)
            {
                // 利用するAPIを指定(ファイル取得)してURLを作成する
                string api = "/api/filesystem/apps/file";
                string url = "http://" + Target_ipaddress + api;
                
                // リクエストに設定するパラメータを指定する
                Dictionary<string, string> parameters = new Dictionary<string, string>()
                {
                    { "knownfolderid", "Pictures" },
                    { "packagefullname", "\\\\" },
                    { "path", "\\\\" }
                };

                // Http アクセスに HttpClient を利用する
                HttpClient client = new HttpClient();

                // アップロードするファイルコンテンツを取得する
                MultipartFormDataContent content = new MultipartFormDataContent();
                StreamContent fileContent = new StreamContent(System.IO.File.OpenRead(a_UploadFilePath));

                // ファイル名を取得する
                string filename = System.IO.Path.GetFileName(a_UploadFilePath);
                
                // コンテンツ情報を追加したヘッダーを作成する
                // 以下のコードを使うと文字化けする
                //fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
                //{
                //    Name = System.IO.Path.GetFileName(uploadFilepath),
                //    FileName = System.IO.Path.GetFileName(uploadFilepath)
                //};
                // https://qiita.com/kawaidainfinity/items/4aee443bd77a9888cc9c を参考に手動エンコード
                string headerValue = string.Format("form-data; name=\"{0}\"; filename=\"{1}\"", filename, filename);
                byte[] headerValueByteArray = Encoding.UTF8.GetBytes(headerValue);
                StringBuilder encodingHeaderVaule = new StringBuilder();
                foreach (byte b in headerValueByteArray)
                {
                    encodingHeaderVaule.Append((char)b);
                }
                fileContent.Headers.Add("Content-Disposition", encodingHeaderVaule.ToString());
                content.Add(fileContent);

                // URLを作成する
                string urlRequest = string.Format("{0}?{1}", url, new FormUrlEncodedContent(parameters).ReadAsStringAsync().Result);

                // POSTメソッドのリクエストメッセージを作成
                HttpRequestMessage request = new HttpRequestMessage
                {
                    Method = HttpMethod.Post,
                    RequestUri = new Uri(urlRequest),
                    Content = content
                };

                // ヘッダに Basic 認証を設定
                // ユーザー名とパスワードを指定
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue
                (
                    "Basic",
                    Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", Target_username, Target_password)))
                );

                // リクエストの実行
                HttpResponseMessage response = client.SendAsync(request).Result;

                // レスポンスコードを表示
                // リクエスト成功時は OK (200=正常)が表示される
                LogMessage(response.StatusCode.ToString());
                LogMessage(response.ToString());
            }

            /// <summary>
            /// ログメッセージの処理関数
            /// </summary>
            /// <param name="message">ログメッセージ</param>
            private void LogMessage(string message)
            {
                // ログメッセージはコンソールに表示する
                System.Diagnostics.Trace.WriteLine(message);
                Console.WriteLine(message);
            }
        }
    }
}

アクセスする HoloLens に合わせて以下の変数は変更してください。

target_ipaddress = "127.0.0.1:10080";
target_username = "USERNAME";
target_password = "PASSWORD";

127.0.0.1:10080 のアドレス(USB接続)で HoloLens にアクセスする方法は以下を参照ください。
bluebirdofoz.hatenablog.com

本プログラムは引数で、アップロードするファイルのパスを指定します。
VisualStudio 上で動作させる場合は以下の手順で、実行時の引数を指定します。

メニューから デバッグ -> HoloLensAPIConsoleのプロパティ を選択します。
f:id:bluebirdofoz:20190304093534j:plain

プロパティ画面が開くので、[デバッグ]タブを選択します。
[開始オプション]の[コマンドライン引数]に、実行時の引数を書き込みます。
f:id:bluebirdofoz:20190304093547j:plain

アップロードするファイルパスはコンソールアプリを実行するローカルPCのファイルパスを指定します。
f:id:bluebirdofoz:20190304093557j:plain

これで引数の設定は完了です。
メニューから デバッグ -> デバッグなしで開始 を選択して処理を実行します。
f:id:bluebirdofoz:20190304093606j:plain

コンソールが開き、プログラムが実行されます。
処理が成功すれば「OK」の文字がコンソールに出力されます。
f:id:bluebirdofoz:20190304093617j:plain

HoloLens の Pictures ディレクトリを確認すると、ファイルがアップロードされています。
f:id:bluebirdofoz:20190304093629j:plain

コンソールアプリの実行

作成したアプリケーションはプロジェクトフォルダの bin ディレクトリ配下に exe ファイルの形式で出力されています。
f:id:bluebirdofoz:20190304093644j:plain

PowerShell などで実行可能です。
f:id:bluebirdofoz:20190304093701j:plain

参考ページ

qiita.com
qiita.com