Flutter - 로컬 데이터베이스 ④: 이미지 파일을 데이터베이스에 저장하고 읽기

2 분 소요

목차

1: Flutter - 로컬 데이터베이스 ①: sqflite(생성, 삽입, 업데이트, 삭제, 쿼리)

  • sqflite를 사용하여 Specs(id, type, category, method, contents, money, dateTime)라는 로컬 데이터베이스를 생성
  • 로컬 데이터베이스에 접근하는 SpecProvider 클래스를 생성하고, DB에 읽고 쓰는 함수들을 정의

2: Flutter - 로컬 데이터베이스 ②: Future(데이터베이스 화면에 띄우기, FutureBuilder)

  • DB에 접근하는 것은 async한 작업
  • 이를 Future 키워드를 사용해 다루고, FutureBuilder로 화면에 값을 보여줌

3: Flutter - 로컬 데이터베이스 ③: Singleton, factory

  • DB 인스턴스를 Singleton으로 생성

4: Flutter - 로컬 데이터베이스 ④: 이미지 파일을 데이터베이스에 저장하고 읽기 <현재>

  • 이미지 파일을 데이터베이스에 저장하고 읽기


참고한 스택 오버플로우

이미지를 데이터베이스에 저장하자


이미지 데이터베이스에 저장하기

클래스 생성

일단 이미지 클래스를 만들자
현재 만들고 있는 앱은 각 spec 마다 이미지가 여러 장 추가될 수 있다. 따라서 Picture(id, specID, picture)로 생성하기로 한다. id는 이미지의 id로, 삭제할 때 접근하는 Primary Key다. specID는 각 spec들이 가진 고유한 id로, spec에 포함된 이미지들을 찾을 때 접근한다. picture는 저장될 실제 데이터다.

class Picture {
  int? id;
  int specID;
  Uint8List picture;

  Picture({this.id, required this.specID, required this.picture});

  Map<String, dynamic> toMap(){
    return {
      "id": id,
      "specID": specID,
      "picture" : picture,
    };
  }
}

생성자와 데이터베이스에 저장해주기 위해 Map으로 변환해주는 함수를 정의한다.


PicProvider 클래스, db 테이블 생성

class PicProvider {
  static final PicProvider _picProvider = PicProvider._internal();
  PicProvider._internal(){
    // init values...
    
    /*
    async cannot be used in constructor
    */
  }
  factory PicProvider() {
    return _picProvider;
  }

  static Database? _database;

  Future<Database> get database async => _database ??= await initDB();

  initDB() async {
    String path = join(await getDatabasesPath(), 'Pics.db');

    return await openDatabase(
        path,
        version: 1,
        onCreate: (db, version) async {
          await db.execute('''
            CREATE TABLE Pics(
              id INTEGER PRIMARY KEY AUTOINCREMENT,
              specID INTEGER NOT NULL,
              picture BLOB NOT NULL
            )
          ''');
        },
        onUpgrade: (db, oldVersion, newVersion){}
    );
  }

  Future<void> insert(Picture pic) async {
    final db = await database;
    print("Pics insert ${pic.specID}");
    pic.id = await db?.insert(picTableName, pic.toMap());
  }

  Future<void> delete(Picture pic) async {
    final db = await database;
    print("Pics delete ${pic.specID}");
    await db?.delete(
      picTableName,
      where: "id = ?",
      whereArgs: [pic.id],
    );
  }

  Future<void> deleteSpec(int specID) async {
    final db = await database;
    print("Pics delete all ${specID}");
    await db?.delete(
      picTableName,
      where: "specID = ?",
      whereArgs: [specID],
    );
  }

  Future<List<Picture>> getDB() async {
    final db = await database;
    final List<Map<String, dynamic>> maps = await db!.query(picTableName);
    if( maps.isEmpty ) return [];
    List<Picture> list = List.generate(maps.length, (index) {
      return Picture(
        id: maps[index]["id"],
        specID: maps[index]["specID"],
        picture: maps[index]["picture"],
      );
    });
    return list;
  }

  Future<List<Picture>> getQuery(String query) async {
    final db = await database;
    final List<Map<String, dynamic>> maps = await db!.rawQuery(query);
    if( maps.isEmpty ) return [];
    List<Picture> list = List.generate(maps.length, (index) {
      return Picture(
        id: maps[index]["id"],
        specID: maps[index]["specID"],
        picture: maps[index]["picture"],
      );
    });
    return list;
  }
}

생성과 insert, delete 등의 함수들은 이전 포스트와 비슷하다.


이미지 Uint8List로 변환하기

XFile? image = await picker.pickImage();
picProvider.insert(Picture(specID: spec.id!, picture: await image.readAsBytes()));

image picker로 받아온 이미지들은 XFile 타입이다. 이것을 readAsBytes()로 읽어 Uint8List로 변환할 수 있다.

단, readAsBytes()의 반환 타입은 Future<Uint8List>이므로 await해서 기다려 줘야 한다.


Uint8List로 이미지 만들기

Image.memory(Uint8List bytes);

// in this database
Picture pic;
Image.memory(pic.picture)


XFile로 이미지 만들기

Image.file(File(image.path))



굿

댓글남기기