Edited at

UnityからAmazon S3にファイルをアップロードし、公開状態にする

More than 1 year has passed since last update.


実現する事

Unityで以下を行う。


  1. ファイルをAmazon S3にアップロードし、公開状態にする

  2. 公開URLを作る


確認環境


  • MacBook Pro(OS X 10.11.5)

  • iPhone 6s

  • Unity v5.4.0f3

  • Xcode v7.3.1


関連ドキュメント

この記事は以下のAWS公式ドキュメントの手順に沿って進めているが、同ドキュメントは若干説明不足だったり例が間違っている箇所があるので、そこは適宜補っている。

Set Up the AWS Mobile SDK for Unity — Unity Developer Guide

Amazon Simple Storage Serivce (S3) — Unity Developer Guide


(1) AWS Mobile SDK for UnityをダウンロードしてUnityにimportする


  1. AWS Mobile SDK for Unityをダウンロードする。

    ※このダウンロードリンクは後述の上記ドキュメントから直接リンクされているものなので、リンク切れの場合はドキュメントを確認する


  2. zipを展開すると、利用できる各サービスの名前が入った.unitypackageファイルが並ぶ


  3. Unityを起動し、S3を扱いたいプロジェクトを開く。この記事では新規プロジェクトを作成した


  4. 展開した中の AWSSDK.S3.<バージョン番号>.unitypackage をimportする。当該ファイルをFinder上でダブルクリック、もしくはUnityのAssetsメニュー -> Import Package -> Cutom Package で当該ファイルを選択する


  5. 全てチェックした状態でimport実行

    ※Examplesは本番では不要



(2) SDKの設定ファイルを用意する

SDKの設定、及びビルド周りで必要なlink.xmlの設定を行う。


awsconfig.xml

SDK全体に関わる設定は awsconfig.xml に記載する。この設定はプログラムで行う事も出来るので必須ではない(ファイルが無くて全てプログラム制御でも動作する)。

公式ドキュメントにあるサンプルのXMLは文法エラーがあり、それを直すだけだとやはりエラーになる(少なくとも手元の環境では)。以下は、修正の上で動作確認ができたもの。なおawsconfig.xmlは Resources ディレクトリ以下に置かないと読み込まれない。


awsconfig.xml

<?xml version="1.0" encoding="utf-8"?>

<aws region="ap-northeast-1" correctForClockSkew="true">
<logging logTo="UnityLogger"
logResponses="Always"
logMetrics="true"
logMetricsFormat="JSON" />
<s3 useSignatureVersion4="true" />
</aws>

correctForClockSkew="true" が無いと初期化時にNullReferenceExceptionが起きる。これを追加するのは、 https://github.com/aws/aws-sdk-net/issues/332 を参考にした。


link.xml

ビルド時のストリッピング回避のため、link.xml を設定する。なおlink.xml自体の説明は Unity - マニュアル: ビルドした iOS プレイヤーのサイズ最適化 などを参照。このファイルの置き場は Assets 以下であればどこでも良い(今回はAssets直下に置いた)。


link.xml

<linker>

<assembly fullname="UnityEngine">
<type fullname="UnityEngine.Experimental.Networking.UnityWebRequest" preserve="all" />
<type fullname="UnityEngine.Experimental.Networking.UploadHandlerRaw" preserve="all" />
<type fullname="UnityEngine.Experimental.Networking.UploadHandler" preserve="all" />
<type fullname="UnityEngine.Experimental.Networking.DownloadHandler" preserve="all" />
<type fullname="UnityEngine.Experimental.Networking.DownloadHandlerBuffer" preserve="all" />
</assembly>
<assembly fullname="mscorlib">
<namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>
<assembly fullname="System">
<namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>
<assembly fullname="AWSSDK.Core" preserve="all"/>
<assembly fullname="AWSSDK.CognitoIdentity" preserve="all"/>
<assembly fullname="AWSSDK.SecurityToken" preserve="all"/>
</linker>


(3) Identity Pool IDを取得する



  1. Cognito Consoleを開く


  2. Manage Federated Identities をクリック


  3. Create new identity pool をクリック


  4. 以下を入力して Create Pool をクリック


    Identity pool name : 任意の名前

    Enable access to unauthenticated identities : チェックする




  5. 許可 をクリック。なお以下のスクショは詳細を表示した状態


  6. Platform 選択で Unity を選ぶ。その状態で Get AWS Credentials 欄に表示されているものを、後でUnityのC#スクリプト内で利用する


CognitoAWSCredentials credentials = new CognitoAWSCredentials (

"ap-northeast-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", // Identity Pool ID
RegionEndpoint.APNortheast1 // Region
);


(4) S3の設定を行う


バケットの作成

※既に利用するバケットがある場合にはこの手順は不要。



  1. S3 consoleを開く


  2. バケットを作成 をクリック


    バケット名 : 他と被らない任意の名前(この記事では unity-upload)

    リージョン : 任意(この記事では Tokyo)




  3. 作成をクリック



IAMの設定



  1. IAM Console を開く

  2. 左のメニューから ロール をクリック


  3. Cognito_<Identity pool nameに入れた名前>Unauth_Role をクリック(似た名前の xxxAuth_Role もあるので注意)


  4. ロールポリシーの作成 をクリック


  5. Policy Generator の枠内にある 選択 をクリック


    効果 : 許可

    AWSサービス : Amazon S3

    アクション : すべてのアクション をチェック

      ※ポリシー次第。後述の「おまけ」も参照

    Amazon リソースネーム(ARN) : arn:aws:s3:::<バケット名>/*

      ※例 arn:aws:s3:::unity-upload/*




  6. ステートメントを追加 をクリック


  7. 次のステップ をクリック


  8. ポリシーの適用 をクリック

    ※ポリシー次第ではここで書き換える。後述の「おまけ」も参照



(5) C#スクリプトでの実装


スクリプト追加


  1. S3を扱いたいSceneに、空のGame Objectを作成する

  2. このGame ObjectのInspectorから、Add Componentで任意の名前をつけたC#スクリプトを追加する


実装


初期化処理

Start()またはAwake()に以下の処理を追加する

// using Amazon;

UnityInitializer.AttachToGameObject(this.gameObject);

// (2016/09/27追記) DeleteObjectなど幾つかのリクエストは、UnityWebRequestを指定しないとエラーになる。
// 詳しくは本記事末尾に記載した。
AWSConfigs.HttpClient = AWSConfigs.HttpClientOption.UnityWebRequest;

// using Amazon.CognitoIdentity;
CognitoAWSCredentials credentials = new CognitoAWSCredentials (
"ap-northeast-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", RegionEndpoint.APNortheast1
);

credentialsの部分は、Identity Pool IDを取得するで作成したもの。

ここまでは、S3以外のAWSサービスを利用する場合と共通となる。


S3クライアント作成処理

AmazonS3Client S3Client = new AmazonS3Client (credentials, RegionEndpoint.GetBySystemName(RegionEndpoint.APNortheast1.SystemName));

RegionEndpointはawsconfig.xmlで指定している場合は不要。両方指定した場合はコードで指定したものが優先される。

AmazonS3ClientのAPIドキュメントは AmazonS3Client Class | AWS SDK for .NET V3 参照。


S3へのファイルアップロード処理

今回は例として、Assets/StreamingAssets ディレクトリに test_image.jpg を置き、それををアップロードする。

アップロードの際にACL指定で公開(PublicRead)を指定しているので、アップロード完了のタイミングで外部からアクセスできるようになる。

string S3BucketName = "unity-upload";

string fileName = "test_image.jpg";

var stream = new FileStream(Application.streamingAssetsPath +
Path.DirectorySeparatorChar + fileName,
FileMode.Open, FileAccess.Read, FileShare.Read);

var request = new PostObjectRequest()
{
Bucket = S3BucketName,
Key = fileName,
InputStream = stream,
CannedACL = S3CannedACL.PublicRead // 公開状態
};

S3Client.PostObjectAsync(request, (responseObj) =>
{
if (responseObj.Exception == null)
{
// success
}
else
{
// fail
}
});


(6) 公開URLを作る

本来であればSDKから取得したいが、AmazonS3Clientにそれらしきメソッドが見つからなかった。レスポンスにも入っていないようだったので、単純に文字列で作る。

リージョンを考えない最も汎用的と思われるS3の公開URLは


https://<バケット名>.s3.amazonaws.com/<ファイル名>


なので、今回の例は以下となる。


https://unity-upload.s3.amazonaws.com/test_image.jpg



おまけ:IAMのロールポリシー設定

SDKの公式ドキュメントで例示されているポリシー設定のARN arn:aws:s3:::examplebucket/* はちょっと罠があって、これだとexamplebucket内のPutObject/GetObjectなどはできますが、examplebucket内のオブジェクト一覧取得(ListBucket)はAccess Deniedとなってしまいます。

今回実現したい事はPutObject/PutObjectAclが出来れば良いのでこれでも問題無いのですが、最初試した時に同ドキュメントのListBucket項でいきなりエラーになってしまいました。

結果としては、AWS リソースの管理に関するポリシーの例 - AWS Identity and Access Managementで説明されているポリシー設定を真似て解決しました。


  • リスト取得はバケット操作なので、バケット自体(unity-upload)を指定する

  • 各オブジェクトの操作は、バケットの中(unity-upload/*)を指定する

  • PutObjectAcl は公開状態にするために必要

なお arn:aws:s3:::* と指定すればどちらも含む事ができますが、見ての通りunity-upload以外のバケットも対象になってしまうので、運用方法によってはNGではないかと思います。

{

"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::unity-upload"
]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::unity-upload/*"
]
}
]
}


追記


2016/09/27 UnityWebRequestの指定について

初期化処理に、HttpクライアントとしてUnityWebRequestを利用する指定を追加しました。

サンプルで挙げているPostObjectやListObjectsなどは特にこれが無くても問題無いのですが、DeleteObjectなど幾つかのリクエストは、このUnityWebRequest指定が無いと以下のようなエラーとなります。


InvalidOperationException: DeleteObject is only allowed with AWSConfigs.HttpClientOption.UnityWebRequest API option

Amazon.S3.AmazonS3Client.DeleteObjectAsync (Amazon.S3.Model.DeleteObjectRequest request, Amazon.Runtime.AmazonServiceCallback`2 callback, Amazon.Runtime.AsyncOptions options)

(以下略)


公式の説明は以下にあります。

Unity V3 support in the AWS SDK for .NET is out of preview - AWS Developer Blog - Mobile