How insert image in room persistence library?

51,958

Solution 1

It is usually not recommended to store image data into the database. But however if it is required for your project then you can do so.

Image data are usually stored into db using BLOB data type, Room also provide support for BLOB data type Documentation

You can declare your entity class as mentioned below to store Image data.

@Entity(tableName = "test")
public class Test{

@PrimaryKey
@ColumnInfo(name = "_id")
private int id;

@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
private byte[] image;
}

Solution 2

As Pinakin mentioned, it is not recommended to store an image into database and file path would be better but if it is required to store image I would suggest compress the image to below 2 MB (here is an example) to avoid breaking app. Room supports BLOB for image. Entity class in kotlin:

ImageTest.kt

 @Entity    
 class ImageTest {

        @PrimaryKey(autoGenerate = true)

        var id: Int = 1

        @ColumnInfo(typeAffinity = ColumnInfo.BLOB)
        var data: ByteArray? = null
      }

ImageDao.kt

 @Dao
 interface ImageTestDao {

       @Insert(onConflict = OnConflictStrategy.REPLACE)
       fun upsertByReplacement(image: List<ImageTest>)

       @Query("SELECT * FROM image")
       fun getAll(): List<ImageTest>

       @Query("SELECT * FROM image WHERE id IN (:arg0)")
       fun findByIds(imageTestIds: List<Int>): List<ImageTest>

       @Delete
       fun delete(imageTest: ImageTest)
   }

Databse.kt

 import android.arch.persistence.room.Database
 import android.arch.persistence.room.RoomDatabase
 import android.arch.persistence.room.TypeConverters

   @Database(entities = arrayOf(ImageTest::class), version = 1)
   @TypeConverters(DataConverters::class)
   abstract class Database : RoomDatabase() {
    abstract fun getImageTestDao(): ImageTestDao
   }
    

In DatabaseHelper something like

  class DatabaseHelper(context: Context) {

   init {
        DatabaseHelper.context = WeakReference(context)
        }

   companion object {
    
   private var context: WeakReference<Context>? = null
   private const val DATABASE_NAME: String = "image_test_db"
   private var singleton: Database? = null

   private fun createDatabase(): Database {
       return Room.databaseBuilder(context?.get() ?:
               throw IllegalStateException("initialize by calling  
               constructor before calling DatabaseHelper.instance"),
               Database::class.java,
               DATABASE_NAME)
               .build()
   }


   val instance: Database
       @Synchronized get() {
           if (null == singleton)
               singleton = createDatabase()

           return singleton as Database
       }

     fun setImage(img: Bitmap){
     val dao = DatabaseHelper.instance.getImageTestDao() 
     val imageTest = ImageTest()
     imageTest.data = getBytesFromImageMethod(image)//TODO
     dao.updsertByReplacement(imageTest)

     fun getImage():Bitmap?{
     val dao = DatabaseHelper.instance.getImageTestDao() 
     val imageByteArray = dao.getAll()
     return loadImageFromBytes(imageByteArray[0].data)
     //change accordingly 
     }

Correct me if I am wrong. Hope this helps someone out there

Solution 3

Save the image as a file and save the file path Uri to Room

As seen in CameraX's image capture use case, when a photo is successfully taken, the File path reference Uri, savedUri, can be retrieved safely.

Then, the Uri can be converted to a string with savedUri.toString(), and saved to Room.

  • It's important to ensure the Room file reference is also updated if the file is moved or deleted.
  • The image String saved in Room may need to be converted back into a Uri to be displayed with an image library such as Glide with Uri.parse(someString).

In the CameraX sample, an image path's Uri can safely be obtained in onImageSaved.

  • It would then be saved into Room off of the main thread using Kotlin Coroutines or RxJava, preferably in a ViewModel or somewhere that handles the business logic separate from the view logic.

Getting Started with CameraX > 5. Implement ImageCapture use case

private fun takePhoto() {
   // Get a stable reference of the modifiable image capture use case
   val imageCapture = imageCapture ?: return

   // Create time-stamped output file to hold the image
   val photoFile = File(
       outputDirectory,
       SimpleDateFormat(FILENAME_FORMAT, Locale.US
       ).format(System.currentTimeMillis()) + ".jpg")

   // Create output options object which contains file + metadata
   val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()

   // Set up image capture listener, which is triggered after photo has
   // been taken
   imageCapture.takePicture(
       outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
           override fun onError(exc: ImageCaptureException) {
               Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
           }

           override fun onImageSaved(output: ImageCapture.OutputFileResults) {
               val savedUri = Uri.fromFile(photoFile)
               val msg = "Photo capture succeeded: $savedUri"
               Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
               Log.d(TAG, msg)
           }
       })
}

This strategy is outlined in Saving image in Room database on Reddit.

Cloud Storage

Creating a file for the image and saving the file path in Room covers local storage. In order to ensure the images are saved across multiple devices or when if data cache and data are cleared, a form of Cloud Storage is needed to upload the files to and to download and sync with the local storage.

Share:
51,958
Prince Kumar
Author by

Prince Kumar

Updated on July 09, 2022

Comments

  • Prince Kumar
    Prince Kumar almost 2 years

    I am using room persistence library for my android application, Now I have to insert image in my db. I successfully define @Entity for the primitive data type. and also through converter class, i stored all object, date, time. Now I have to store Image. I am not able to understand how we define Column info and entity and how we insert that data as well as read data from the table.

    What is the maximum size of data which inserted into the single row? What is max and min size of data in one field in Android SQLite?

  • Prince Kumar
    Prince Kumar over 6 years
    Thanks for explanation of each line and easy to understandable solution my whole project in java.
  • Prince Kumar
    Prince Kumar over 6 years
    Thanks its working. i need to add encoder and decoder which convert raw image to byte[] and byte[] to raw image
  • Pinakin Kansara
    Pinakin Kansara over 6 years
    @PrinceKumar True, you need encoder and decoder for bitmap to byte[] and vice a versa.
  • Jhon Fredy Trujillo Ortega
    Jhon Fredy Trujillo Ortega over 6 years
    I cant uset Blob type?
  • Pinakin Kansara
    Pinakin Kansara over 6 years
    @JhonFredyTrujilloOrtega, If you don't want to use blob then, compress your image first then convert it to "base 64" string and store it in db. Storing either "blob" or "base 64" is not recommended.Best is to store file locally in to file system & only add reference entry in db. its up to your requirement though.
  • Elad Lavi
    Elad Lavi about 6 years
    It seems as if this solution isn't truly using BLOB. This solution makes the app hold the byte array completely in memory whereas a true us of BLOB, such as in SQLite or MySQL, would use InputStream to write and OutputStream to write. So if the byte array is too large, only a buffer of it is held in memory at a time. By the way, BitmapFactory supports working with InputStream. Is ROOM database missing this feature?
  • user7168064
    user7168064 almost 5 years
    @ColumnInfo(typeAffinity = ColumnInfo.BLOB) private byte[] image; must be : @ColumnInfo(name="your_name") private byte[] image; I works for me
  • Fatih
    Fatih about 3 years
    What are the advantages or disadvantages of using (typeAffinity = ColumnInfo.BLOB)? I can already save a byte array to Room without giving this type.