💾

画像を "Firestore" に保存する

2022/09/12に公開

やること

画像をBase64文字列化してFirestoreのフィールドに保存する。

Base64って?

簡単に言えば、画像などのデータを文字列として出力すること。容量はオリジナルよりも若干増加する。

有効なケース

サムネイルが付随するドキュメントや、アイコンを有するユーザーデータの場合、サムネイルやアイコンをStorageに保存するよりも、Base64化してそれぞれのFirestoreのフィールドに保存してしまった方がコスト的にも有利だと思う。

有効ではないケース

キャッシュを活用したい場合や、画像へのリンクが必要な場合には向かないかも。あとは複数のドキュメントで画像が更新される可能性がある場合も。

やってみる

  • Flutterで取得した画像をBase64化する
  • Firestoreに書き込み
  • Firestoreから取得して表示

1.画像を取得してBase64にエンコード

実際に使うのはこのUnSplashの画像です。

解像度 容量
1920 × 1080 135KB

ユーザーの端末から取得したあとに、そのままBase64に変換します。実際にサムネイルにするなら、もうすこし小さなサイズに圧縮するべきかもしれませんが。

import 'package:file_picker/file_picker.dart';
import 'dart:convert';

FilePickerResult? result = await FilePicker.platform.pickFiles();

if (result != null) {
  File file = File(result.files.single.path);
  Uint8List? bytes;
    if (kIsWeb) {
      bytes = file.files.first.bytes;
    } else {
      File fileData = File(file.files.first.path!);
      bytes = fileData.readAsBytesSync();
    }
  final String base64Image = base64Encode(bytes!);
}

容量は約170KB程度になりました。

2.Firestoreに書き込み

文字列として書き込みます。ドキュメントの最大容量は1MBなので、これくらいの画像ならその他のデータと一緒に格納できます。

import 'package:cloud_firestore/cloud_firestore.dart';

final docData = {'base64Str': base64Image}
await FirebaseFirestore.instance
            .collection('posts')
            .doc('hogehoge')
	    .set(docData)


こんな感じで保存されます。

3.Firestoreから取得して表示

import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';

class Base64Image extends StatelessWidget {
  const Base64Image({Key? key, })
      : super(key: key);

  
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: FirebaseFirestore.instance
            .collection('posts')
            .doc('hogehoge')
            .get(),
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return const SizedBox();
          }
          final doc = snapshot.data!.data();
          final base64Str = doc!['base64Str'] as String;
          final decoded = const Base64Decoder().convert(base64Str.split(',').last);
          return Image.memory(
            decoded,
          );
        });
  }
}

まとめ

少なくとも、ユーザーアイコンはこの方法で問題ない気がしますがどうなんでしょうか🤔 
なんか落とし穴があったらすいません🙏

Discussion