FlutterWebでフォルダから画像を取得してきてFirebaseStorageにアップする

# FlutterWebでフォルダから画像を取得してきてFirebaseStorageにアップする

どうもミヤジックです！
今日はFlutterウェブで画像をパソコン内のフォルダから取得してきてFirebaseStorageにアップする方法を紹介していきます。

すでにFlutterWebのアプリが動いていて、Firebaseとの連携もできている前提で話を進めます。



今回僕がやりたかったことを図示しました。
![スクリーンショット 2020-12-19 14.26.05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935513/226a15ff-a6c3-a0a3-3ec5-57db36609296.jpeg)

後から図にしてみるとそんなに難しい事はしていませんが、少しつまづいてしまったので、記事にすることにしました。

※本記事は[こちら](https://www.youtube.com/watch?v=ZlqXDbn6jg0&ab_channel=LearnFlutterCode "参考動画")の動画を参考に書いたものです。
※本記事は[こちら](https://www.youtube.com/watch?v=ZlqXDbn6jg0&ab_channel=LearnFlutterCode "参考動画")の動画を参考に書いたものです。
大事な事なので2回言いました。
動画見た方がわかりやすいかもです。
英語恐怖症の方と動画は長くて見れられない人はこの記事を読んでいただけたらと思います。

※また、本記事を作成した際のGitHubRepositoryは[こちら](https://github.com/miyaji555/flutter_web_image_picker "GitHub")

それでは行きましょ〜

#目次
[1.FirebaseStorageに保存した画像を表示する。](#1-firebasestorageに保存した画像を表示する)
[2.フォルダから画像の取得](#2-フォルダから画像の取得)
[3.FireStorageへのアップロード](#3-firestorageへのアップロード)
[4.一連の流れをスムーズにする。](#4-一連の流れをスムーズにする)
[5.まとめ](#5-まとめ)


###1. FirebaseStorageに保存した画像を表示する。
FirebaseStorageをウェブで開きファイルをアップロードから画像ファイルを選択してアップロードします。![スクリーンショット 2020-12-19 15.00.59.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935513/05057a75-9ae6-806d-66fa-38fa83a51112.jpeg)
アップロードをした画像を選択すると、右下のファイルの場所からURLが取得できます。しかし、取得できるURLはgs://から始まっており、https://に変換してやる必要があります。

```
  Future downloadUrl() async {
    imageURL = await storage()
        .refFromURL('gs://flutterwebimagepicker.appspot.com/')
        .child('イラスト屋/oomisoka_yoiotoshio_summer_woman.png')
        .getDownloadURL();
    print(imageURL);
    notifyListeners();
  }
```
上記の関数でimageURLにhttpsから始まるURLが取得できます。printしたURLをブラウザに打てばFireStorebに保存した画像が見れるはずです。

これをアプリ内で表示したいので、NetworkImageウィジェットにimageURLを渡します。

```
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  model.imageURL == null
                      ? SizedBox()
                      : Container(
                          width: 100,
                          height: 100,
                          padding: EdgeInsets.all(10.0),
                          decoration: BoxDecoration(
                            color: Colors.white,
                            shape: BoxShape.circle,
                            image: DecorationImage(
                                fit: BoxFit.cover,
                                image: NetworkImage(model.imageURL.toString())),
                          ),
                        ),
                  RaisedButton(
                    color: Colors.green,
                    onPressed: () {
                      model.downloadUrl();
                    },
                  )
                ],
              ),
```

また、imageURLがNullのときは何も表示しないSizedboxウィジェットを表示させています。
RaisedButtonを押すことで、先ほどのdownloadUrlが呼ばれ、imageURLに適切なUriが返されます。また、NetworkImageはString型しか受け付けてくれないのでtoStringでキャストしてあげます。

これで画像が表示されました。
![スクリーンショット 2020-12-19 15.42.22.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935513/67e40712-e3cd-c13c-1ac7-37b8a8aec604.jpeg)



###2. フォルダから画像の取得
次に画像の取得です。ここでの画像の取得はローカルのフォルダから画像をとってくることを意味します。
まず準備としてdart:htmlをインポートします。これはInputElementを用いるためです。

```
import 'dart:html';
```
次にローカルフォルダの画像ファイルを取得するpickupImage関数を作成します。

```
  void pickupImage() {
    InputElement uploadInput = FileUploadInputElement()..accept = 'image/*';
    uploadInput.click();

    uploadInput.onChange.listen((event) {
      final file = uploadInput.files.first;
      final reader = FileReader();
      reader.readAsDataUrl(file);
      reader.onLoadEnd.listen((event) {
        print('done');
      });
    });
  }
```
ポイントは一行目の'image/*'で画像以外のファイルを選択できないようにしている点です。

![onlyPicker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935513/152dce5f-7196-11a8-d323-cac4561659f4.gif)





###3. FireStorageへのアップロード

先ほどはFirebaseStorageへ手動でアップロードしましたが、今回上で取得した画像ファイルをアプリ内でFirebaseStorageにアップロードします。

先ほど取得したファイルをFirebaseにアップロードするためにuploadToStorage関数を作成します。

```
  void uploadToStorage() {
    pickupImage(
      onSelected: (file) {
        storage()
            .refFromURL('gs://flutterwebimagepicker.appspot.com/')
            .child('イラスト屋/oomisoka_yoiotoshio_summer_woman.png')
            .put(file);
      },
    );
  }
```
また、pickupImageを書き換える必要もあります。

```
  void pickupImage({@required Function(File file) onSelected}) {
    InputElement uploadInput = FileUploadInputElement()..accept = 'image/*';
    uploadInput.click();

    uploadInput.onChange.listen((event) {
      final file = uploadInput.files.first;
      final reader = FileReader();
      reader.readAsDataUrl(file);
      reader.onLoadEnd.listen((event) {
        onSelected(file);
      });
    });
  }
```
ここはやっていることが少し複雑なのですが、pickupImageの引数として「File型のfileという引数を持つ関数」を要求させています。
uploadToStorageでpickupImageを呼ぶ際には

```
      onSelected: (file) {
        storage()
            .refFromURL('gs://flutterwebimagepicker.appspot.com/')
            .child('イラスト屋/oomisoka_yoiotoshio_summer_woman.png')
            .put(file);
      },
```
これがpickupImageに渡されています。そして、

```
      reader.onLoadEnd.listen((event) {
        onSelected(file);
      });
```
この行で関数が呼び出されています。引数はもちろんfileです。

これで画像がfirebaseにアップロードされます。
また、画像を再取得することでアップロードした画像を表示できました。
![pickerAndUpload.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935513/35fff02b-8ab7-6cb7-5926-506b0d059aa6.gif)



###4. 一連の流れをスムーズにする。

基本的なやりたい事はここまででできたので、これらの流れをボタン一つで行えるようにします。

画像を押すと一連の流れが行えるようにInkwellウィジェットを用いてonTapが使えるようにしました。

```

           InkWell(
                    child: model.imageURL == null
                        ? SizedBox()
                        : model.isLoading
                            ? CircularProgressIndicator()
                            : Container(
                                width: 100,
                                height: 100,
                                padding: EdgeInsets.all(10.0),
                                decoration: BoxDecoration(
                                  color: Colors.white,
                                  shape: BoxShape.circle,
                                  image: DecorationImage(
                                      fit: BoxFit.cover,
                                      image: NetworkImage(
                                          model.imageURL.toString())),
                                ),
                              ),
                    onTap: () {
                      model.uploadToStorage();
                    },
                  ),
```

またbool型のisLoadingを用意する事で、画像のアップロードと表示までの間はCircularProgressIndicatorを表示しています。

uploadToStorageの中でpickupImageとdownloadUrlを呼び出すのですが、画像のアップロード完了を待ってからdownloadUrlをするために非同期処理を書いています。非同期処理については[こちら](https://www.youtube.com/watch?v=IFBVoQSei88&ab_channel=KBOY%E3%81%AEFlutter%E5%A4%A7%E5%AD%A6 "同期処理と非同期処理")


```
  Future<void> uploadToStorage() async {
    final dateTime = DateTime.now();
    final path = 'dxOmk3FX0W3kywxAf29a/$dateTime';
    _pickupImage(
      onSelected: (file) async {
        isLoading = true;
        notifyListeners();
        await storage()
            .refFromURL('gs://flutterwebimagepicker.appspot.com/')
            .child('イラスト屋/$path')
            .put(file)
            .future;
        await _downloadUrl(path);
        isLoading = false;
        notifyListeners();
      },
    );
  }
```

完成した実装がこちら
![完成.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935513/0d485dee-b266-38d9-bd6d-7a1ce4d53b6f.gif)

###5. まとめ

なんとか画像をfirebaseにアップして表示することができました。
非同期処理の辺りやpickしてきた画像をfirebaseにそのままアップロードする辺りが少し難しかったです。

最後まで読んでいただきありがとうございます。本記事が皆様のお助けになれば幸いです。
それでは皆様良きFlutterLifeを！！