- 投稿日:2020-06-28T18:49:07+09:00
FlutterでWeb,Android,iOSの3プラットフォームから同じコードでS3に写真をアップロードする
概要
6月にWeb向けのImagePickerが出ていたので、ネイティブアプリとブラウザアプリを同じソースコードでビルドして、どのデバイスでもファイルアップロード出来るのかというのを試しました。
AndroidとiOSに加えてWebでもPickerが同じ処理で使えるようになったのは結構画期的だなと思ってのメモです。内容はクライアント側に重点を置いています。1. パッケージをインポートする。
pubspec.yamlにimage_pickerとimage_picker_for_web、amazon_cognito_identity_dart_2を追加する。
私が試したバージョンは以下です。image_picker: ^0.6.7 image_picker_for_web: ^0.1.0+1 amazon_cognito_identity_dart_2: ^0.1.142. 画像取得処理を書く。thenを使用してますが、awaitでも問題ありません。
import 'package:image_picker/image_picker.dart'; ~ ~ var picker = ImagePicker(); picker.getImage(source: ImageSource.gallery) .then((PickedFile value) { // ここから必要に応じて後続の処理を呼び出す });3. アップロード処理を書く。
こちら( https://pub.dev/packages/amazon_cognito_identity_dart_2#signing-requests )のページのFor S3 Uploadsをベースにしますが、PickedFileはlengthが取れないので少し改造します。PolicyやSigV4はそのままで
main()
だけ変更します。
私の場合はuploadImage
として以下のようにしました。Future<String> uploadImage(PickedFile file) async { final _credentials = CognitoCredentials(ID_POOL_ID, _userPool); final _cognitoUser = CognitoUser(_username, _userPool); final authDetails = AuthenticationDetails(username: _username, password: _passwd); CognitoUserSession _session; try { _session = await _cognitoUser.authenticateUser(authDetails); } catch (e) { print(e); return null; } await _credentials.getAwsCredentials(_session.getIdToken().getJwtToken()); final uri = Uri.parse(_s3Endpoint); final req = http.MultipartRequest("POST", uri); final multipartFile = http.MultipartFile.fromBytes('file', await file.readAsBytes(),contentType: MediaType('application', 'octet-stream')); logger.d('Policy.fromS3PresignedPost'); var uuid = Uuid(); String filename = 'test.jpg'; final policy = Policy.fromS3PresignedPost( filename, _backetName, 150, _credentials.accessKeyId, 10000000, _credentials.sessionToken, region: REGION); final key = SigV4.calculateSigningKey( _credentials.secretAccessKey, policy.datetime, REGION, 's3'); final signature = SigV4.calculateSignature(key, policy.encode()); req.files.add(multipartFile); req.fields['key'] = policy.key; req.fields['acl'] = 'public-read'; req.fields['X-Amz-Credential'] = policy.credential; req.fields['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256'; req.fields['X-Amz-Date'] = policy.datetime; req.fields['Policy'] = policy.encode(); req.fields['X-Amz-Signature'] = signature; req.fields['x-amz-security-token'] = _credentials.sessionToken; try { final res = await req.send(); logger.d('res:${res.toString()}'); await for (var value in res.stream.transform(utf8.decoder)) { logger.d(value); } return filename; } catch (e, stacktrace) { logger.e(e); logger.e(stacktrace); } return null; } }ID_POOL_IDや_userPoolは各環境に合わせて定義してください。
fromS3PresignedPostへ渡すパラメータmaxFileSizeは一旦10Mとしているので必要に応じて変更してください。
リンク先と主に異なるのは、final file = File(path.join('/path/to/my/folder', 'square-cinnamon.jpg'));
がPickedFile
に置き換わっていて、
streamからMultipartFileを生成する処理final stream = http.ByteStream(DelegatingStream.typed(file.openRead())); final multipartFile = http.MultipartFile('file', stream, length, filename: path.basename(file.path));が
bytesからMultipartFileを生成する処理final multipartFile = http.MultipartFile.fromBytes('file', await file.readAsBytes(),contentType: MediaType('application', 'octet-stream'));になっております。目的としてはlengthが取れないからbytesでのリクエストにしました。
クライアント側はこんな感じです。
他に
- Cognitoのユーザプール作成
- IDプール作成
- IAM追加
- S3のCORS設定
- iOSのNSPhotoLibraryUsageDescription
が必要です。特にS3のCORS設定はブラウザからのみアップロードできない問題に直面するので、ハマりポイントとして記載します。(その他は調べれば出てくると思うので他の方の記事をご参照ください。)
4. サーバ側番外編 S3でCORSの設定
こちら( https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/cors.html )のCORS設定例を参考にS3のバケット>アクセス権限>CORSの設定で設定してください。
AllowedOrigin
だけ各環境に合わせて変える必要があります。
ローカル環境からの試しであればアスタリスク*
で設定すると早いです。<?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedOrigin>https://example.org</AllowedOrigin> <AllowedMethod>HEAD</AllowedMethod> <AllowedMethod>GET</AllowedMethod> <AllowedMethod>PUT</AllowedMethod> <AllowedMethod>POST</AllowedMethod> <AllowedMethod>DELETE</AllowedMethod> <AllowedHeader>*</AllowedHeader> <ExposeHeader>ETag</ExposeHeader> <ExposeHeader>x-amz-meta-custom-header</ExposeHeader> </CORSRule> </CORSConfiguration>
- 投稿日:2020-06-28T17:34:16+09:00
Unity で Automatically manage signing を行う
はじめに
Unity & Xcode でビルドを作成する際に毎回忘れてしまう署名関連の備忘録として残しておこうと思います。
Manual or Auto
『Certificates, Identifiers & Profiles』の署名関係のファイルなどを Xcode だけで出来るようになっています。
以前までは自前でポータルサイトにログインしてそれぞれのファイルを作成していましたが、今はそれが不要になっています。
エンタープライズ版の場合は * アスタリスクでどのアプリでも利用することができたのですが、今はそれを行う必要がなく Unity を利用する場合は Preferences と PlayerSettings に設定するだけです。注意
Automatically manage signing を行う場合にポータルで同じ Bundle ID のプロビジョニングプロファイルなどの作成はしないようにしてください。もし、同じ Bundle ID が存在すると競合してしまいエラーになります。
Unity 側の設定
Unity 側には二箇所設定する項目があります。
Preferences の設定
まずは Preferences ですが『Xcode Default Settings』という設定がありますが、それぞれ以下のような値を設定します。
- Automatically Sign にチェック
- Signing Team Id にポータルの Team ID を指定
- iOS/tvOS の Manual は利用しないのでそのまま
Team ID はポータルのアカウントで『Membership』に記載されています。
PlayerSettings の設定
次はプロジェクトごとの PlayerSettings です。
『PlayerSettings > Other Settings > Identification』で以下のような値を設定します。
- Bundle Identifier に Bundle ID
- Signing Team ID に Team ID を指定
- Automatically Sign にチェック
Xcode Default Settings の値と同じにします。
まぁ、設定しなくてもデフォルト設定が利用されると思うのですが…。ビルド
Unity の設定を行いビルドを行います。
生成された Xcode プロジェクトを開いて『Signing & Capabilities』の設定を見るとちゃんと設定がされています。ポータル側も以下のように証明書、プロファイルがそれぞれ登録されています。
『Certificates,Identifiers & Profiles > Certificates』にはビルドを実行した端末が登録されています。
『Certificates,Identifiers & Profiles > Identifiers』には『XC <Bundle ID>』という ID が登録されます。
例えば『com.aaa.BBB』とした場合『XC com aaa BBB』という感じの ID です。
おわりに
とりあえず毎回忘れてしまう署名関連の処理。
Unity を使えば Unity の設定だけで済みますのでかなり楽になります。一度 Manual ではなく Auto でビルドすることを検討してみてください。
- 投稿日:2020-06-28T01:00:50+09:00
iOS アプリ起動時に "The Google Mobile Ads SDK was initialized without an application ID" で強制終了する場合に必要な対応
概要
iOS アプリ起動直後に以下のようなログを出力して強制終了する件について、原因と対応方法をまとめました。
*** Terminating app due to uncaught exception 'GADInvalidInitializationException', reason: 'The Google Mobile Ads SDK was initialized without an application ID. Google AdMob publishers, follow instructions at https://googlemobileadssdk.page.link/admob-ios-update-plist to set a valid application ID. Google Ad Manager publishers, follow instructions at https://googlemobileadssdk.page.link/ad-manager-ios-update-plist.'対応方法
ログに含まれている URL を開き、そこに書かれている記載に従い、
Info.plist
の修正をする必要があります。Google AdMob
Google AdMob を利用している場合には、以下リンク先のスタートガイドの記載に従い、
Info.plist
にGADApplicationIdentifier
キーと、それに対応する値( AdMob アプリ ID )を追加します。
https://googlemobileadssdk.page.link/admob-ios-update-plist追加するキーと値の例<key>GADApplicationIdentifier</key> <string>ca-app-pub-3940256099942544~1458002511</string>Google Ad Manager
Google Ad Manager を利用している場合には、以下リンク先のスタートガイドの記載に従い、
Info.plist
にGADIsManagerApp
キーと、それに対応する値YES
を追加します。
https://googlemobileadssdk.page.link/ad-manager-ios-update-plist追加するキーと値の例<key>GADIsAdManagerApp</key> <true/>この問題の背景
上記2つのスタートガイドの両方に記載されているとおり、 Google Mobile Ads SDK のバージョン
7.42.0
以降では、Info.plist
に必要な値を設定しておかないとアプリが強制終了します。アプリの
Info.plist
に AdMob アプリ ID を設定する必要があることについては、 Google Mobile Ads SDK のバージョン7.42.0
のリリースノートにも書かれています。