[Xamarin.Mac] AWS SDKでS3 Bucket内のオブジェクトを一覧してみました

2020.12.10

1 はじめに

CX事業本部の平内(SIN)です。

Xamarin.Macを使用すると、C#でネイティブなMacのアプリが作成可能です。 ここでは、私自身がXamarin.Macに入門して学習した事項を覚書として書かせて頂いています。

今回は、特定のS3バケットのオブジェクトを一覧してみました。

2 NuGetパッケージ

Visual Studioでは、パッケージの管理は、NuGetで行うことができます。

検索で、「awssdk」と入れてみると、作成者が、Amazon Web Servicesとなった公式のSDK(パッケージ)が検索できます。

今回追加したのは、S3CognitoIdentityの2つですが、依存関係で必要なパッケージも一緒にダウンロードされます。

3 Cognito

プログラムからS3にアクセスするために、CognitoでプールIDを作成しています。

ロールに付与したパーミッションは、特定のバケットのオブジェクト一覧と、取得のみになっています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": "arn:aws:s3:::xamarin-s3-sample"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:getObject"
            ],
            "Resource": "arn:aws:s3:::xamarin-s3-sample/*"
        }
    ]
}

4 S3アクセス

Cognitoの認証でS3バケットにアクセスするために作成したクラスは、以下のとおりです。

コンストラクタで、Cognito認証によるS3クライアントを生成し、2つのメソッド(一覧取得とオブジェクト取得)が利用可能になっています。

public class S3Client {
    // Cognito Pool ID
    private string poolId = "ap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
    // アクセス先のバケット
    private string bucketName = "xamarin-s3-sample";

    private AmazonS3Client client;

    public S3Client() {
        // クレデンシャル取得
        var cognitoCredentials = new CognitoAWSCredentials(poolId, RegionEndpoint.APNortheast1);
        // S3クライアントの生成
        client = new AmazonS3Client(cognitoCredentials);
    }

    // バケット内のオブジェクト一覧取得
    public async System.Threading.Tasks.Task<List<string>> getObjectsAsync() {
        var result = await client.ListObjectsAsync(bucketName);
        return result.S3Objects.Select(o => o.Key).ToList();
    }

    // 指定したオブジェクトの取得
    public async System.Threading.Tasks.Task<string> getObjectAsync(string key) {
        var request = new GetObjectRequest {
            BucketName = bucketName,
            Key = key
        };
        var response = await client.GetObjectAsync(request);
        var reader = new StreamReader(response.ResponseStream);
        return reader.ReadToEnd();
    }
}

5 画面構成

メインとなるウインドウは、NSSplitViewで3分割され、下のウインドウの左側にNSCollectionView、右側にNSTextViewを配置しています。 それぞれ、CollectionViewとTextViewという名前でOutletが作成されています。

コレクションビューのアイテム用ビューは、Labelが、ファイル名表示用にバインディングされています。

6 DataSource/Delegate

コレクションビューのデータソースは、バケット内のオブジェクト一覧を保持するリストFilesで動作しています。

public class CollectionViewDataSource: NSCollectionViewDataSource {
    public List<string> Files = new List<string>();

    public CollectionViewDataSource() {
    }

    public override NSCollectionViewItem GetItem(NSCollectionView collectionView, NSIndexPath indexPath) {
        var item = collectionView.MakeItem("cell", indexPath) as ItemViewController;
        item.FileName = Files[(int)indexPath.Item];
        return item;
    }

    public override nint GetNumberofItems(NSCollectionView collectionView, nint section) {
        return Files.Count;
    }
}

デリゲートでは、コンストラクタでViewControllerをコピーしておいて、選択が変化したタイミングで、ItemSelectedAsync()を呼び出しています。

public class CollectionViewDelegate: NSCollectionViewDelegateFlowLayout {
    ViewController ViewController;
    public CollectionViewDelegate(ViewController ViewController) {
        this.ViewController = ViewController;
    }

    public override void ItemsSelected(NSCollectionView collectionView, NSSet indexPaths) {

        var paths = indexPaths.ToArray<NSIndexPath>();
        _ = ViewController.ItemSelectedAsync((int)paths[0].Item);

    }
}

7 ViewController

メインとなるViewControllerです。

S3を操作するクラスをコンストラクタで生成しています。

起動時、若しくはReloadボタンが押されたタイミングで、バケットの一覧を取得して、左側のコレクションビューを初期化します。また、コレクションビューの選択が変化したタイミングで、右側のテキストビューを更新しています。

public partial class ViewController : NSViewController {

        S3Client s3client;

        public ViewController(IntPtr handle) : base(handle) {
            s3client = new S3Client();
        }

        public override void ViewDidLoad() {
            base.ViewDidLoad();

            // PictureViewControllerの登録
            CollectionView.RegisterClassForItem(typeof(ItemViewController), "cell");

            // DataSource
            CollectionView.DataSource = new CollectionViewDataSource();

            // Flow layout
            var flowLayout = new NSCollectionViewFlowLayout() {
                ItemSize = new CGSize(100, 100),
                SectionInset = new NSEdgeInsets(10, 10, 10, 20),
                MinimumInteritemSpacing = 10,
                MinimumLineSpacing = 10
            };
            CollectionView.WantsLayer = true;
            CollectionView.CollectionViewLayout = flowLayout;

            // Delegate
            CollectionView.Delegate = new CollectionViewDelegate(this);

            //Reloadボタン
            ReloadButton.Activated += ReloadButton_Activated;

            //Reload
            _ = ReloadObjectsAsync();

        }

        // Reloarボタンのアクション
        private void ReloadButton_Activated(object sender, EventArgs e) {
            _ = ReloadObjectsAsync();
        }

        // リロード(一覧の再取得)
        async Task ReloadObjectsAsync() {
            ((CollectionViewDataSource)CollectionView.DataSource).Files = await s3client.getObjectsAsync();
            CollectionView.ReloadData();
        }

        // 選択が変化した際に呼び出せれる
        public async Task ItemSelectedAsync(int index) {
            var key = ((CollectionViewDataSource)CollectionView.DataSource).Files[index];
            Console.WriteLine(key);
            var text = await s3client.getObjectAsync(key);
            Console.WriteLine(text);
            TextView.Value = text;
        }

// ・・・略・・・

8 最後に

AWS SDKは、DotNet対応版が揃っており、Visual Studioでは、これをNuGetで簡単に導入できます。

おかげで、Xamarin.Macからでも、AWSのリソースの利用は、非常に簡単だと思います。