はじめに
S3
で追加した画像をFlutterで表示する方法がわからず苦労したので、
解決方法を記事にしたいと思います。
今回の記事では、S3にアクセスするのにminioというパッケージを使用しています。
Flutterで、AWS のサービス・リソースにアクセスするときに使うライブラリは、公式にはamplify使用するのがスタンダードみたいで、S3
へのアクセスにはamplify_storage_s3を使用するみたいですが、
amplify
の事前準備が面倒に思えたのでminio
を使ってます。
また解説はFlutterの環境設定とコードのみとなります。
S3
での画像追加方法については割愛しますのでご了承ください。
目次
設定
こちらの記事を参照しました。
一部重複するところがありますがご了承ください。
1 pubspec.yamlにパッケージを追加
以下のパッケージを追加してください
・minio
→ S3にアクセスするために使う
・flutter_dotenv
→ S3アクセスに必要な情報を定義したenvファイルを読み込むために使用する
2 envファイルを読み込むためにassetsを編集する
assets:
- .env.dev // この行を追加
3 envファイルを作成する
プロジェクト直下(pubspec.yamlと同階層)に.env.devを作成する
内容は下記参照。
END_POINT=s3-ap-northeast-1.amazonaws.com
REGION=ap-northeast-1
ACCESS_KEY=アクセスキー
SECRET_KEY=シークレットキー
BUCKET=バケット名
-
アクセスキー
S3にアクセスできるIAMユーザーのアクセスキー
わからない場合、またはない場合は下記の手順で作成してください- AWSのコンソールにログイン
- サービス一覧からIAMに移動
- S3にアクセスできるIAMユーザーを表示
- 認証情報を確認
- アクセスキーの作成
- csvをダウンロード ※外部に公開しないよう厳重に管理
-
シークレットキー
上記で作成したアクセスキーのCSVに載ってます -
バケット名
オブジェクト(画像ファイル)を追加したバケットの名前
4 envファイルをGit管理から外す
※ソースをGit管理している人は.gitignoreに下記を追加してください
ソースをPushする際には、作成したenvファイルがコミットに含まれていないか確認お願いします
認証情報が外部に漏れる可能性があるため、Gitには上げない方がいいと思います
.env.dev
.env
S3にアクセスするためのコードを実装
1 envファイルを読み込み
flutter_dotenv
をインポートし、void main()
のメソッド内のrunApp
の前に以下の一行を追加してください。
import 'package:flutter_dotenv/flutter_dotenv.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 下記の一行を追加
await dotenv.load(fileName: ".env.dev");
runApp(const ProviderScope(child: MyApp()));
}
2 S3を操作するMinioを取得するコードを実装
Minioを取得するシングルトンのクラスとして実装してみました。
class S3 {
Minio? _minio;
S3._();
static final instance = S3._();
Minio getMinio(){
_minio ??= Minio(
endPoint: dotenv.env['END_POINT']!,
region: dotenv.env['REGION']!,
accessKey: dotenv.env['ACCESS_KEY']!,
secretKey: dotenv.env['SECRET_KEY']!,
useSSL: true,
);
return _minio!;
}
}
S3から画像を取得し、表示するコードを実装
本記事の肝の部分です。
1 S3から画像を取得し表示するWidget
Widget化してみましたので、適宜カスタマイズしてお使いください。
デバッグはあまりしてないので、不具合等あるかもしれませんがご了承ください。
あと、main.dart
のインポート部分は、S3にアクセスするためのコードを実装の2 S3を操作するMinioを取得するコードを実装で作成したクラスを定義したファイルをインポートしてください。(私は横着してmain.dartに実装してます)
使い方は、画像表示したい箇所でImageFromS3(bucketName: 'バケット名', objectName: 'オブジェクト名')
を呼んでください。
import 'dart:typed_data';
import 'package:flutter/material.dart';
// S3クラスを定義したファイルをインポート
import '../main.dart';
class ImageFromS3 extends StatelessWidget {
final String bucketName;
final String objectName;
const ImageFromS3({Key? key, required this.bucketName, required this.objectName}) : super(key: key);
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getImage(),
builder: (BuildContext context, AsyncSnapshot<Image> snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
if (snapshot.hasData) {
return Container(child: snapshot.data!,);
} else {
return const Text("データが存在しません");
}
},
);
}
Future<Image> getImage() async {
final minio = S3.instance.getMinio();
final stream = await minio.getObject(bucketName, objectName);
List<int> memory = [];
await for (var value in stream) {
memory.addAll(value);
}
return Image.memory(Uint8List.fromList(memory));
}
}
2 画像を取得し、表示する部分の解説
画像を取得し、Imageウィジェット化している部分は上記コードのgetImage
メソッドです。
まず以下のコードでS3からオブジェクトを取得しています
final minio = S3.instance.getMinio();
final stream = await minio.getObject(bucketName, objectName);
この部分は他の記事でも解説があり、少し調べればたどり着けました。
続いて、取得したオブジェクトをImageウィジェット化するコードが以下です。
(私はこの部分の実装がわからず苦労しました)
List<int> memory = [];
await for (var value in stream) {
memory.addAll(value);
}
return Image.memory(Uint8List.fromList(memory));
まず、minio.getObject
メソッドでバイトデータのMinioByteStreamが返ってくるので、
バイトデータからImageウィジェット
を作成するために、Imageウィジェット
を作成するメソッドの一つであるImage.memory
を使えば画像を表示できるのでは?と当りを付けました。
ただ、MinioByteStream
をどうやってバイトデータに変換すればいいのかわからず、Stream
について調べたところ、
await for
を使えば、Stream
の終了を待てるという記事(dart の stream を理解して async* と yield を正しく使う)があったので、
試しに実装してみたところうまく表示できました。
少しでもご参考になりましたら、いいね押していただけると励みになります( `・∀・´)ノヨロシク