aws sdk cppを利用してTexture2DをpngでAWS S3に[uuid].pngの名前でアップロード(Bucket名は決め打ち)しPre-SignedURLを取得する機能を提供するプラグインを作成してみました。
Windows64bit環境にしか対応してません。
提供された機能はBlueprintで以下のように利用できます。
サンプルプロジェクトは以下に載せてみています。
https://github.com/mechamogera/UE4S3UploadPlugin
ここではプラグイン作成についてメモします。
2022/10/11追記
aws-sdk-cppの更新をしようとしてはまった内容を以下の記事にまとめました。
利用環境
- UE4.23.1
- aws-sdk-cpp 1.7.235
aws sdk cppのビルド
基本的にはhttps://github.com/aws/aws-sdk-cpp のReadme参照
- GitForWindowsなどでコマンドプロンプトでgitを使えるようにしておく(cmake中に依存をcloneするため)。
- Githubからaws-sdk-cppを取得し適切な場所(msbuildの際パスが260文字以上になると失敗する)に配置。
- スタートメニューからVisualStudio2017フォルダ中のDeveloper Command Promptを起動する。
- cmake -G "Visual Studio 15 Win64" -DCMAKE_BUILD_TYPE=Release を実行する。
- に移動してmsbuild INSTALL.vcxproj /p:Configuration=Release を実行する。
aws sdk cppのビルドでまったところ
ビルドの際にC2220エラーが出るので聞いてみたら丁寧に教えてもらえました。
https://github.com/aws/aws-sdk-cpp/issues/1286
結局テスト時の失敗だったので無視することにしました。
プラグインの作成
UE4のPluginsの設定からNew PluginでBlankのTemplateを利用してベースを作成しています。
プラグインの構成
プラグインの構成は以下のようになります。
Plugins/AWSSDK/
├── AWSSDK.uplugin
├── Binaries
│ └── Win64
│ ├── aws-c-common.dll <= [aws sdk cpp]/binからコピー
│ ├── aws-c-event-stream.dll <= [aws sdk cpp]/binからコピー
│ ├── aws-checksums.dll <= [aws sdk cpp]/binからコピー
│ ├── aws-cpp-sdk-core.dll <= [aws sdk cpp]/binからコピー
│ ├── aws-cpp-sdk-s3.dll <= [aws sdk cpp]/binからコピー
├── Content
├── Resources
│ └── Icon128.png
├── Source
│ └── AWSSDK
│ ├── AWSSDK.Build.cs
│ ├── Private
│ │ ├── AWSSDK.cpp
│ │ └── S3UploadProxy.cpp
│ └── Public
│ ├── AWSSDK.h
│ └── S3UploadProxy.h
└── ThirdParty
└── awssdk
├── include <= [aws sdk cpp]/aws-cpp-sdk-core/includeと[aws sdk cpp]/aws-cpp-sdk-s3/includeからコピー
│ └── aws
│ ├── core
│ └── s3
└── lib
├── aws-cpp-sdk-core.lib <= [aws sdk cpp]/aws-cpp-sdk-core/Releaseからコピー
└── aws-cpp-sdk-s3.lib <= [aws sdk cpp]/aws-cpp-sdk-s3/Releaseからコピー
AWSクレデンシャル
AWSクレデンシャルはビルド時に環境変数を読み込んで利用するようにしています。
Plugins/AWSSDK/Source/AWSSDK/AWSSDK.Build.cs
以下の記事を参照しています。
string AwsAccessKeyId = System.Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID");
string AwsSecretAccessKey = System.Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY");
Definitions.Add(string.Format("AWS_ACCESS_KEY_ID=\"{0}\"", AwsAccessKeyId));
Definitions.Add(string.Format("AWS_SECRET_ACCESS_KEY=\"{0}\"", AwsSecretAccessKey));
本当はクレデンシャルを直に利用するよりCognitoなどを利用する方が良さそうです。
Blueprintの提供について
UOnlineBlueprintCallProxyを利用してDelegateを直に利用しなくても良いようにして提供しています。
Plugins/AWSSDK/Source/AWSSDK/Public/S3UploadProxy.h
WebApi プラグインの設計思想 & 実装Tips - ほげたつブログあたりを参照しています。
非同期処理
aws sdk cppにも非同期なメソッドが用意されていますがひとまず今回はUE4が用意している非同期処理を使ってみました。
書籍「C++でつくるUnreal Engineアプリ開発 for Windows & macOS」を参照してAsyncメソッドを使っているのですが、最近使い方が変わったようではまっていました。
4.23 breaks my Async code... - Unreal Engine Forums見て対応できました。
Plugins/AWSSDK/Source/AWSSDK/Private/S3UploadProxy.cpp
TUniqueFunction<void()> Task = [this, BucketName, ObjectName, ImageData, RegionStr]() {
// 略
};
auto Result = Async(EAsyncExecution::ThreadPool, MoveTemp(Task));
非同期処理ではまったこと1
aws sdk cppのサンプルを参考にして以下のようなコードを書いているとエディタが落ちました。
Aws::SDKOptions options;
Aws::InitAPI(options);
{
// 非同期処理呼び出し
}
Aws::ShutdownAPI(options);
非同期処理を呼び出した後にShutdownAPI()してしまうのでS3アップロードのためのPutObject()が失敗するというオチでしたがしばらくはまりました。
AWS SDK for C++をマルチスレッドで利用する際の注意点 - Qiitaを参照しました。
結局InitAPI()とShutdownAPI()の処理をStartupModule()とShutdownModule'(で実行するようにしました。
Plugins/AWSSDK/Source/AWSSDK/Private/AWSSDK.cpp
非同期処理ではまったこと2
以下のような感じでUtexture2Dからpngへ変換したデータを保持するTArrayを非同期タスクの呼び出し前にローカルに宣言してポインタだけ渡していたため、ローカル変数が破棄されてポインタが指す正常でない値をpngとして扱っていました。
Task()
{
TArray<uint8> Buffer;
// Utexture2Dからpngへ変換してBufferにしまう
AsyncTask((char*)Buffer.GetData(), Buffer.Num());
}
void AsyncTask(char* Buffer, int BufferSize)
{
}
UTexture2Dからpngへの変換
以下のサイトのコードを参照してオンメモリでUTexture2Dからpngへの変換を行っています。
UTexture2D 转为png - qq_26384541的博客
パッケージ化
パッケージ化するとaws sdk cppのdllをexeと同じ場所におかないとアプリが実行できないという問題が起きました。
How do you link to a DLL from a plugin? - UE4 AnswerHubを参照してFPlatformProcess::GetDllHandle()を使ってDLLをロードするようにして解消できました。
なお、DLLは依存順(依存のないDLLから読み込みし、読み込まれたDLLに依存しているDLLを読み込んでいく)にGetDllHandle()していく必要があります。
Plugins/AWSSDK/Source/AWSSDK/Private/AWSSDK.cpp
そういうのも含めてBuild.csの書き方は苦労しました。
(PublicDelayLoadDLLsやRuntimeDependenciesでパスを指定する/しないなど)
.gitignore
Plugins/AWSSDK/Binaries/Win64にaws sdk cppのdllを置いているために.gitignoreに以下のような設定を追加してビルドしたプラグインのdllがリポジトリに含まれないようにしています。
(開発中にBinaries/Win64に置いてないとdllがなくてエディタが落ちるからしていたけど最終的に明示的にdllロードするようにしたから他の場所においてもいいかも?)
Plugins/**/BInaries/*
!Plugins/**/Binaries/Win64
Plugins/**/Binaries/Win64/*
!Plugins/**/Binaries/Win64/aws-*.dll
[Git] .gitignoreの仕様詳解 - Qiitaを参照しています。
プラグインビルド時にはまったこと
プラグインのコードを変更してビルドする時に以下のようなエラーで失敗することがたびたび起きました。
cl : Command line error D8022 : cannot open E:\Hoge\Plugins\AWSSDK\Intermediate\Build\Win64\UE4Editor\Development\AWSSDK\Module.AWSSDK.cpp.obj.response'
@progress 'Compiling C++ source code...' 29%
[2/7] Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27034 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
cl : Command line error D8022 : cannot open 'E:\Hoge\Plugins\AWSSDK\Intermediate\Build\Win64\UE4Editor\Development\AWSSDK\Module.AWSSDK.gen.cpp.obj.response'
@progress 'Compiling C++ source code...' 43%
[3/7] Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27034 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
cl : Command line error D8022 : cannot open 'E:\Hoge\Plugins\AWSSDK\Intermediate\Build\Win64\UE4Editor\Development\AWSSDK\AWSSDK.cpp.obj.response'
@progress 'Compiling C++ source code...' 57%
以下の2つのファイルを一旦他の場所に移してビルドして再び戻して再ビルドすると成功していました。
- Plugins/AWSSDK/Source/AWSSDK/Private/S3UploadProxy.cpp
- Plugins/AWSSDK/Source/AWSSDK/Public/S3UploadProxy.h
もしくは、プロジェクトのIntermediate、Build。Binariesあたりを削除してプラグインとプロジェクト本体を再ビルドするといけるみたいです。
原因がよくわかっていません。