読者です 読者をやめる 読者になる 読者になる

未踏作業日誌――余計なもの作るよ!

未踏の作業日誌的なものを書きましょうということで書くことにしました.余計なことばっかりしています.

UnityでキャプチャしたスクリーンショットをAmazon S3にアップロードしてみる――だができない!

Unity上で撮ったスクリーンショットAmazon S3にアップロードして,そのURLをTweetボタンに貼り付けて投稿する,ということをやりたかった.

普段なら,Apacheでサーバを立ててアップロード作るみたいなノリなんだけれども,AWS SDKの.NET版を使えばUnityだけで完結できそうな感じだったのでちょっとやってみた.

結論から言うと,AWSSDKそのものがUnityで使えないことがわかった.

 

Unityで撮ったスクリーンショットをAmazon S3にアップロードする

 

スクリーンショット自体は,他のGameObjectがクリックされた時にBroadcastMessageでCapture関数を呼ぶ形にしている.

AccessKeyとSecretKeyは別ファイルにしてる.Resourcesにそれぞれを書いたテキストファイルを読み込んでるだけ.

 

AWSSDK.dllは.NET 3.5版で,Asset下に置くと自動的に参照設定をしてくれる.

で,S3へ接続してアップロードするのは案外簡単だったりする.

  1. AWSClientFactory.CreateAmazonS3Clientクラスのインスタンスを作る.こいつはS3へ接続するためのクライアントクラス.
  2. TransferUtilityクラスのインスタンスを作る.TransferUtilityはAWSへ接続するためのクラス.1番のクライアントを渡すことで接続が可能になる.
  3. TransferUtilityUploadRequestクラスのインスタンスを作る.AWSへ接続するにはまずリクエストが必要になる.リクエストの種類には色々あるけど,今回はその中でもアップロードするためのインスタンスを作っている.バケット名やアップロードするファイルなどを指定する.
  4. TransferUtility.Upload関数に3番のリクエストを投げればアップロードされる.アップロードに失敗した場合は例外AmazonS3Exceptionが投げられる.

構造としてはクライアント,接続,リクエストの3つのクラスの関係を覚えるだけでいい.非常にわかりやすい.

 

しかし,ソースコードそのままやってみたもののどうやら失敗するらしい.アップロードのところをtry-catchで囲んでいるけれども,AmazonS3Exceptionではなく,SecurityExceptionが投げられる.原因が今ひとつわからない.

ちなみに,こんな例外が投げられた.とてつもなく長い.

SecurityException: Unexpected error while trying to call method_GetSecurityPolicyBlocking : System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Net.WebException: Error getting response stream (Write: The authentication or decryption has failed.): SendFailure ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b010a
  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.validateCertificates (Mono.Security.X509.X509CertificateCollection certificates) [0x00000] in <filename unknown>:0
  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.ProcessAsTls1 () [0x00000] in <filename unknown>:0
  at Mono.Security.Protocol.Tls.Handshake.HandshakeMessage.Process () [0x00000] in <filename unknown>:0
  at (wrapper remoting-invoke-with-check) Mono.Security.Protocol.Tls.Handshake.HandshakeMessage:Process ()
  at Mono.Security.Protocol.Tls.ClientRecordProtocol.ProcessHandshakeMessage (Mono.Security.Protocol.Tls.TlsStream handMsg) [0x00000] in <filename unknown>:0
  at Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0
  at System.Net.HttpWebRequest.GetResponse () [0x00000] in <filename unknown>:0
  at System.Net.WebConnection.DownloadPolicy (System.String url, System.String proxy) [0x00000] in <filename unknown>:0
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object,System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object
parameters, System.Globalization.CultureInfo culture) [0x000d0] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222
  --- End of inner exception stack trace ---
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object parameters, System.Globalization.CultureInfo culture) [0x000eb] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object
parameters) [0x00000] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115
  at UnityEngine.UnityCrossDomainHelper+WebRequestPolicyProvider.GetPolicy (System.String policy_url) [0x00000] in <filename unknown>:0
  at UnityEngine.UnityCrossDomainHelper.GetSecurityPolicy (System.String requesturi_string, IPolicyProvider policyProvider) [0x00000] in <filename unknown>:0
  at UnityEngine.UnityCrossDomainHelper.GetSecurityPolicyForDotNetWebRequest (System.String requesturi_string, System.Reflection.MethodInfo policyProvidingMethod) [0x00000] in <filename unknown>:0
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object,System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object
parameters, System.Globalization.CultureInfo culture) [0x000d0] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222
  --- End of inner exception stack trace ---
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object parameters, System.Globalization.CultureInfo culture) [0x000eb] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object
parameters) [0x00000] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115
  at System.Net.WebConnection.CheckUnityWebSecurity (System.Net.HttpWebRequest request) [0x00000] in <filename unknown>:0
System.Net.WebConnection.LoggedThrow (System.Exception e)
System.Net.WebConnection.CheckUnityWebSecurity (System.Net.HttpWebRequest request)
System.Net.WebConnection.Connect (System.Net.HttpWebRequest request)
Rethrow as WebException: Error: ConnectFailure (Unexpected error while trying to call method_GetSecurityPolicyBlocking : System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Net.WebException: Error getting response stream (Write: The authentication or decryption has failed.): SendFailure ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b010a
Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.validateCertificates (Mono.Security.X509.X509CertificateCollection certificates)
Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.ProcessAsTls1 ()
Mono.Security.Protocol.Tls.Handshake.HandshakeMessage.Process ()
(wrapper remoting-invoke-with-check) Mono.Security.Protocol.Tls.Handshake.HandshakeMessage:Process ()
Mono.Security.Protocol.Tls.ClientRecordProtocol.ProcessHandshakeMessage (Mono.Security.Protocol.Tls.TlsStream handMsg)
Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult)
--- End of inner exception stack trace ---
Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult)
--- End of inner exception stack trace ---
System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult)
System.Net.HttpWebRequest.GetResponse ()
System.Net.WebConnection.DownloadPolicy (System.String url, System.String proxy)
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222)
--- End of inner exception stack trace ---
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object
parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232)
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object parameters) (at /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115)
UnityEngine.UnityCrossDomainHelper+WebRequestPolicyProvider.GetPolicy (System.String policy_url)
UnityEngine.UnityCrossDomainHelper.GetSecurityPolicy (System.String requesturi_string, IPolicyProvider policyProvider)
UnityEngine.UnityCrossDomainHelper.GetSecurityPolicyForDotNetWebRequest (System.String requesturi_string, System.Reflection.MethodInfo policyProvidingMethod)
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object
parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222)
--- End of inner exception stack trace ---
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232)
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object
parameters) (at /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115)
System.Net.WebConnection.CheckUnityWebSecurity (System.Net.HttpWebRequest request) )
System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult)
Amazon.RegionEndpoint.LoadEndpointDefinitionFromWeb ()
Rethrow as AmazonServiceException: Error downloading regions definition file.
Amazon.RegionEndpoint.LoadEndpointDefinitionFromWeb ()
Amazon.RegionEndpoint.GetBySystemName (System.String systemName)
Amazon.Runtime.Internal.Auth.S3Signer.SelectSigner (IRequest irequest, Amazon.Runtime.ClientConfig config)
Amazon.Runtime.Internal.Auth.S3Signer.Sign (IRequest request, Amazon.Runtime.ClientConfig clientConfig, Amazon.Runtime.Internal.Util.RequestMetrics metrics, System.String awsAccessKeyId, System.String awsSecretAccessKey)
Amazon.Runtime.AbstractWebServiceClient.SignRequest (IRequestData requestData)
Amazon.Runtime.AmazonWebServiceClient.InvokeHelper (Amazon.Runtime.Internal.AsyncResult asyncResult)
Amazon.Runtime.AmazonWebServiceClient.Invoke (Amazon.Runtime.Internal.AsyncResult asyncResult)
Amazon.Runtime.AmazonWebServiceClient.Invoke[IRequest,PutObjectRequest] (Amazon.S3.Model.PutObjectRequest request, System.AsyncCallback callback, System.Object state, Boolean synchronized, IMarshaller`2 marshaller, Amazon.Runtime.Internal.Transform.ResponseUnmarshaller unmarshaller, Amazon.Runtime.Internal.Auth.AbstractAWSSigner signer)
Amazon.S3.AmazonS3Client.invokePutObject (Amazon.S3.Model.PutObjectRequest putObjectRequest, System.AsyncCallback callback, System.Object state, Boolean synchronized)
Amazon.S3.AmazonS3Client.PutObject (Amazon.S3.Model.PutObjectRequest putObjectRequest)
Amazon.S3.Transfer.Internal.SimpleUploadCommand.Execute ()
Amazon.S3.Transfer.TransferUtility.UploadHelper (Amazon.S3.Transfer.TransferUtilityUploadRequest request)
Amazon.S3.Transfer.TransferUtility.Upload (Amazon.S3.Transfer.TransferUtilityUploadRequest request)
ScreenShotScript.UploadFile (System.Byte[] png) (at Assets/Scripts/ScreenShotScript.cs:89)
ScreenShotScript.Capture () (at Assets/Scripts/ScreenShotScript.cs:110)
UnityEngine.GameObject:SendMessage(String)
ScreenShotButtonScript:Update() (at Assets/Scripts/ScreenShotButtonScript.cs:24)

かなり上の方でGet Security Policyがどうのこうの言っているので,最初に疑ったのはBucket Policyの設定だった.Bucket Policyとは,バケットごとに設定するアクセスの権限を設定するテキストのようなもので,JSON形式で書かなければならない.

{
    "Version": "2008-10-17",
    "Id": "Policy1389777739828",
    "Statement": [
        {
            "Sid": "Stmt1389777732579",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObjectAcl",
                "s3:GetObject",
                "s3:PutObjectAcl",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::fukuwarai/*"
        }
    ]
}

こんな感じで書けばいいんだけれど,なかなかコイツが難しい.最初のステートメントでSid(ステートメントの名前)を指定し,エフェクトで全部許可する意味のものを書いてる.プリンシパルは多分,このステートメントの対象で,アクションはやっていいことなんだろうな.で,最後にリソースをふくわらいにして終わり.

デフォルトの状態からそれなりに変更を加えたんだけれども,例外が消えない.

その次にやったのがPermissionsのGranteeを増やすこと.とりあえず,Add more permissionsボタンを押してパーミッションを増やし,GranteeをEveryoneにして誰でもアクセスできるようにしてみる.ついているチェックボックスをすべてオンにしてできるかどうかやってみた.

これもやっぱりダメだった.

 

しょうがないので,例外の中で特徴的だった”GetSecurityPolicyBlocking”について調べてみた.Reflectionでバグってるみたいだし,Monoについてさんざん何か書かれているみたいなので何かが悪いんだろう.

結果として引っかかったのは数件で,Blockingの単語は全く引っかからない.どうやら新手のエラーらしい.TransferUtility.Upload関数の中身でバグってるみたいなのでどうにもならない.困った.

 

現状で,JavascriptからS3にアクセスしようとすると,AccessKeyやSecretKeyがデバッグツールで丸見えになるのでそれは避けたい.現状でほとんど穴だらけ状態なのに,秘密鍵とか盗まれたら大変なことになるから困る.

とすると,次はEC2インスタンスを立てて,Unityとそいつで画像の送受信を行い,EC2とS3で画像の受け渡しをする感じになるのか…….まあ,EC2を挟めばDynamoDBと将来的には連携できるようになる……気がする…….