Chapter 33

 05.Storage

kazutxt
kazutxt
2023.03.19に更新

Cloud Storage for Firebase(以下Storage)の概要を確認した上で、Storageとの間でファイルをダウンロード/アップロードする方法を解説します。

概要

Storageは一般的なファイルストレージ機能を提供します。
ユーザからのファイルのアップロード先や、全ユーザに提供する共有ファイルの置き場などに使えます。

有効化手順

Storageの有効化は、Firebaseのサイドメニューから「構築」 > 「Storage」を選択し「始める」をクリックします。

今回は動作を確認するためだけに利用するので、ストレージはテストモードを選択しますが、商用利用等は本番環境モードを選択してください。(細かいセキュリティルールが指定できます)


今後の動作確認用に、ダウンロード用(DL)とアップロード用(UL)のフォルダを作成します。

ダウンロード用のフォルダには、flutter.png(Flutterのアイコン画像)と、hello.txt(「HelloWorld!Storage!!」とだけ書かれたテキストファイル)を配置します。

パッケージのインストール

今回は、firebase_storageのパッケージを利用します。詳細はパッケージの公式サイトを参照してください。
また、path_providerとimage_pickerも利用します。こちらはデバイスへの保存や画像の選択を行う際の補助的に利用するパッケージです。

https://pub.dev/packages/firebase_storage
https://pub.dev/packages/path_provider
https://pub.dev/packages/image_picker

まず、必要なパッケージをインストールします。

pubspec.yaml
# 必要部分のみ抜粋/バージョンは各自の環境に合わせたものを使う
dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^1.19.1
+ firebase_storage: ^10.3.2
+ path_provider: ^2.0.11
+ image_picker: ^0.8.5+3 

続いて、iOS用に設定を記述します。
storageには直接関係はないのですが、ファイル操作時にギャラリーを使うため設定します。

ios/Runner/info.plist
<!-- dictタグ内の追記する部分のみ抜粋-->
+<key>NSPhotoLibraryUsageDescription</key>
+<string>フォトライブラリを利用する理由を記載</string>

実装例

このアプリは、ファイルのダウンロードとアップロードを行います。
ダウンロードでは、あらかじめStorageのDLフォルダに配置しておいたFlutterのアイコンとテキストをダウンロードして画面に表示します。
アップロードでは、image_pickerを使ってファイルを選択し、StorageのULフォルダにファイルをアップロードします。

lib/main.dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
import 'dart:io';
import 'dart:convert';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

// MyApp、MyHomePageはデフォルトから変更がないため省略

class _MyHomePageState extends State<MyHomePage> {
  Image? _img;
  Text? _text;

  // ダウンロード処理
  Future<void> _download() async {
    // ファイルのダウンロード
    FirebaseStorage storage = FirebaseStorage.instance;
    Reference imageRef = storage.ref().child("DL").child("flutter.png");
    String imageUrl = await imageRef.getDownloadURL();
    Reference textRef = storage.ref("DL/hello.txt");
    var data = await textRef.getData();

    // 画面に反映
    setState(() {
      _img = Image.network(imageUrl);
      _text = Text(ascii.decode(data!));
    });

    // 画像ファイルはローカルにもを保存
    Directory appDocDir = await getApplicationDocumentsDirectory();
    File downloadToFile = File("${appDocDir.path}/download-logo.png");
    try {
      await imageRef.writeToFile(downloadToFile);
    } catch (e) {
      print(e);
    }
  }

  // アップロード処理
  void _upload() async {
    // imagePickerで画像を選択する
    final pickerFile =
        await ImagePicker().pickImage(source: ImageSource.gallery);
    if (pickerFile == null) {
      return;
    }
    File file = File(pickerFile.path);
    FirebaseStorage storage = FirebaseStorage.instance;
    try {
      await storage.ref("UL/upload-pic.png").putFile(file);
      setState(() {
        _img = null;
        _text = const Text("UploadDone");
      });
    } catch (e) {
      print(e);
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            // ダウンロードしたイメージとテキストを表示
            children: <Widget>[
              if (_img != null) _img!,
              if (_text != null) _text!,
            ],
          ),
        ),
        floatingActionButton:
            Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
          FloatingActionButton(
            onPressed: _download,
            child: const Icon(Icons.download_outlined),
          ),
          FloatingActionButton(
            onPressed: _upload,
            child: const Icon(Icons.upload_outlined),
          ),
        ]));
  }
}

FloatingActionButtonをダウンロード用とアップロード用に2つ準備し、対応するメソッドを呼び出しています。

ダウンロード

ダウンロードを行っているのは、_downloadメソッドです。
まず、FirebaseStorage.instanceでインスタンスを作成し、storage.refにパスを与えて参照を取得しています。
画像の方は、getDownloadURLでURLを取得し、Image.networkにURLを渡して画面に表示しつつ、imageRef.writeToFileでローカルにファイルとして保存しています。
テキストの方は、データをgetDataメソッドで取得した後に、ascii.decodeを使ってデータをunit8Listから文字列に変換して画面に表示しています。

アップロード

アップロードを行っているのは、_uploadメソッドです。
まず、ImagePickerを使ってアップロードする画像を選択しています。
続いて、ダウンロードと同様にFirebaseStorage.instanceでインスタンスを作成し、storage.refにパスを与えて参照を取得しています。メソッドチェインで続けてputFileメソッドを使い、ファイルをStorageにアップロードしています。

動作イメージ

ダウンロード


最初は画面に何もありませんが、ダウンロードのアイコンをタップすると画面にアイコンと文字列が表示されます。

アップロード


アップロードのアイコンをタップすると、ImagePickerが起動しファイルを選択できるようになります。画像を選択しアップロードが完了すると、完了メッセージ(UploadDone)が画面に表示されます。


Storageでアップロードされたファイルを確認できます。