0

how do i store image in room database, From Json. as i was trying to store with Byte it gives me error - java.lang.NumberFormatException: For input string: "https://upload.wikimedia.org/wikipedia/commons/4/41/Sunflower_from_Silesia2.jpg"

data Class

@Entity(tableName = "actor",indices = arrayOf(Index(value= arrayOf("id"),unique = true)))
data class ActorItem(
    @PrimaryKey(autoGenerate = true)
    val id_:Int,
    @SerializedName("age")
    @ColumnInfo(name = "age")
    val age: String,
    @SerializedName("id")
    @ColumnInfo(name = "id")
    val id: Int,
    @SerializedName("image")
    @ColumnInfo(name="image")
    val image: Byte,
    @SerializedName("name")
    @ColumnInfo(name = "name")
    val name: String
)

here is Json

[
 {"id": 1,
  "name": "Hero",
  "image": "https://upload.wikimedia.org/wikipedia/commons/4/41/Sunflower_from_Silesia2.jpg",
  "age": "23"}
]
Khushbu
  • 88
  • 1
  • 13

4 Answers4

0

Are you trying to store the path to an image in the DB, or the image itself?

If the path, then image would be of type String (not Byte). Storing a path (or name) in the DB, and not the image contents, is generally considered best practice. It requires downloading and saving the image in the filesystem outside of the DB.

If you are trying to store the image contents in the DB though, then the field type would be ByteArray (not Byte), the column type would be Blob, and you will need to download the image and write the bytes in to the DB yourself.

Related:

kaliatech
  • 17,579
  • 5
  • 72
  • 84
  • I m trying to store image itself, but by using ByteArray it give me exception - Expected BEGIN_ARRAY but was STRING path $[0].image – Khushbu Dec 20 '21 at 15:17
0

Simple solution is to store image file in internal directory(App-specific storage) and store the internal private path in your room database column.

Internal App storage Docs :- https://developer.android.com/training/data-storage/app-specific

Or make base64 of file image file and store base64 string in your database column

Muhammad Ahmed
  • 1,038
  • 1
  • 7
  • 7
0

From Json. as i was trying to store with Byte it gives me error - java.lang.NumberFormatException: For input string:

What number, between 0 and 255 does https://upload.wikimedia.org/wikipedia/commons/4/41/Sunflower_from_Silesia2.jpg resolve to 0? 1? 2? .... 255? (Rhetorical) Why? (Rhetorical)

how do i store image in room database

You VERY PROBABLY SHOULD NOT but instead store the image as a file in the Apps's storage (there would be no real difference in storage space) but probably a very noticeable improvement in response times.

The value https://upload.wikimedia.org/wikipedia/commons/4/41/Sunflower_from_Silesia2.jpg is, to many humans, obviously a link to a file that can be downloaded (an image). It does not equate to a byte or any number.

However, the file stored at that location is a series (aka stream) of bytes.

Thus in Room a ByteArray (which Room will assign a type affinity of BLOB the the column). So the column type should either be ByteArray or a type that would require a type converter where the type converter returns a ByteArray.

So instead of val image: Byte, you would probably have val image: ByteArray,.

To get the ByteArray you could (assuming permissions etc are all setup) use something like (but not restricted to):-

return URL(url).readBytes()
  • where url, in your case, would be the String https://upload.wikimedia.org/wikipedia/commons/4/41/Sunflower_from_Silesia2.jpg

IMPORTANT However, at 2.7Mb

enter image description here

that is very likely to cause issues. Not due to SQLite limitations but due to limitations of the Android API which retrieves data from the SQLite database via a Cursor which is a buffer that is limited in size (4Mb). As such any image that is close to 4Mb may be stored but it couldn't be retrieved without complications AND highly inefficient/slow processing.

Demonstration of why NOT to store images, like the one mentioned in the question.** in the database**

As a demonstration consider the following which does store the equivalent of images (not actual images) in the image column of you table

  • (i.e. ByteArrays, the content unless actually displaying the image is irrelevant Room nor SQLite knows the difference between an image and any other BLOB value)

using a slightly modified version of your ActorItem class, as :-

@Entity(tableName = "actor",indices = arrayOf(Index(value= arrayOf("id"),unique = true)))
data class ActorItem(
    @PrimaryKey(autoGenerate = true)
    val id_:Int,
    @ColumnInfo(name = "age")
    val age: String,
    @ColumnInfo(name = "id")
    val id: Int,
    @ColumnInfo(name="image")
    val image: ByteArray,
    @ColumnInfo(name = "name")
    val name: String
) {
    @androidx.room.Dao
    interface Dao {
        @Insert
        fun insert(actorItem: ActorItem)
    }
}
  • i.e. the important difference is a type of ByteArray as opposed to Byte for the image column
  • for brevity/convenience the DAO class has been included (it is sufficient just to insert some columns to demonstrate why saving the image is not a very good idea)

To accompany is an @Database class TheDatabase :-

@Database(entities = [ActorItem::class], version = 1, exportSchema = false)
abstract class TheDatabase: RoomDatabase() {
    abstract fun  getActorItemDao(): ActorItem.Dao

    companion object {
        private var instance: TheDatabase? = null
        fun getInstance(context: Context): TheDatabase {
            if ( instance == null) {
                instance = Room.databaseBuilder(context,TheDatabase::class.java,"thedatabase.db")
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
  • allowMainThreadQueries included for brevity and convenience

Finally putting the above into action via an activity is MainActivity :-

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: ActorItem.Dao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        db = TheDatabase.getInstance(this)
        dao = db.getActorItemDao()
        val url = "https://upload.wikimedia.org/wikipedia/commons/4/41/Sunflower_from_Silesia2.jpg"
        try {
            for (i in 1..100) {
                dao.insert(
                    ActorItem(
                        0, "The age", i,
                        getBitmapFromURLAsString(
                            url,
                            /* The ByteArray  (bitmap) */
                            /* BUT for demonstration of issues associated with size issues
                                start at 1.08Mb size incrementing at 8kb per row
                                last would be 1.8Mb (100 * 8kb)
                             */
                            i * (4096 * 2) + (1024 * 1024)),
                        getNameFromURLAsString(url)
                    )
                )
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    fun getBitmapFromURLAsString(url: String, size: Int): ByteArray {

        /* Fake the byte array allowing the size of the bytearray to be specified */
        val ba = ByteArray(size)
        var byte: Byte = 8
        for(i in 0 until (size)) {
            ba[i] = byte++.mod(Byte.MAX_VALUE)
        }
        return ba
        /* actual would be something like */
        /* WARNING image Sunflower_from_Silesia2.jpg is 2.7Mb will likely cause issues */
        //return URL(url).readBytes()
    }

    fun getNameFromURLAsString(url: String): String {
        val split = url.split("/")
        return split.get(split.size -1)
    }
}

So the activity will try to insert 100 rows with a ByteArray in the image column (answer to how to store image in principle). For each row the size of the ByteArray is increased by 8k (the first row is 1.08Mb i.e. 1Mb and 8k in size). The name column

The above runs successfully without any trapped exceptions. And all 100 rows are inserted:-

enter image description here

using query to extract the length of each image column shows the size (of the last rows) :-

enter image description here

First warning sign that things are perhaps not that good

Running the query takes hardly any time at all. Refreshing, moving from start to end from the table view takes quite a bit of time (a minute (will be dependant upon PC/Laptop used)).

Second warning sign

Running the App takes a few seconds.

Third warning sign

Use i * (4096 * 2) + (1024 * 1024 * 2)), (i.e. start at 2Mb up to 2.8Mb), run the App and try to view via App Inspection and :-

enter image description here

As can be seen the Rows exist and have the expected data in them :-

enter image description here

Try to look at the Actor table, DatabaseInspector doesn't show the contents .

Run the query SELECT substr(image,1024 * 1024) FROM actor (i.e. 8k for the first row 1.8k for the 100th row) WAIT (for a minute or so), scroll to the last, WAIT (for a minutes or so) and :-

enter image description here

MikeT
  • 51,415
  • 16
  • 49
  • 68
0

You should use ByteArray (on java it means byte[]). Sqlite supports saving byte arrays like this: How to store image in SQLite database

And on room database, you can just use ByteArray type for you image, and room will finish the rest of work.

ruby6221
  • 228
  • 1
  • 7