¿Cómo insertar una imagen en la biblioteca de persistencia de la habitación?

6 minutos de lectura

Estoy usando la biblioteca de persistencia de habitaciones para mi aplicación de Android. Ahora tengo que insertar una imagen en mi base de datos. Defino con éxito @Entity para el tipo de datos primitivo. y también a través de la clase de convertidor, almacené todos los objetos, fechas y horas. Ahora tengo que almacenar Image. No puedo entender cómo definimos la información y la entidad de la columna y cómo insertamos esos datos y leemos los datos de la tabla.

¿Cuál es el tamaño máximo de los datos que se insertan en una sola fila? ¿Cuál es el tamaño máximo y mínimo de los datos en un campo en Android SQLite?

  • Puede usar blob para almacenar imágenes en Room.

    – Reina

    21 de septiembre de 2017 a las 7:14

  • Sigue esto desarrollador.android.com/topic/libraries/architecture/room.html

    – Ankita

    21 de septiembre de 2017 a las 7:18

  • @CommonsWare ¿Qué sugiere si no recomienda almacenar imágenes en la base de datos? ¿Almacenar imágenes en almacenamiento externo?

    – Aniruddha

    21 de febrero de 2018 a las 19:36

  • @Aniruddha: ya sea que use Almacenamiento interno o almacenamiento externo depende de si el usuario necesita acceso independiente a las imágenes.

    – CommonsWare

    21 de febrero de 2018 a las 19:50

  • @CommonsWare Sería mejor para fines de aprendizaje si pudiera explicar por qué no recomienda almacenar la imagen en la base de datos.

    – Aniruddha

    22 de febrero de 2018 a las 5:10

Por lo general, no se recomienda almacenar datos de imágenes en la base de datos. Sin embargo, si es necesario para su proyecto, puede hacerlo.

Los datos de imagen generalmente se almacenan en db usando el tipo de datos BLOB, Room también brinda soporte para el tipo de datos BLOB Documentación

Puede declarar su clase de entidad como se menciona a continuación para almacenar datos de imagen.

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

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

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

  • Gracias esta funcionando. necesito agregar un codificador y un decodificador que conviertan la imagen sin procesar en bytes[] y byte[] a la imagen en bruto

    – Príncipe Kumar

    6 de noviembre de 2017 a las 9:52

  • @PrinceKumar Cierto, necesita codificador y decodificador para mapa de bits a byte[] y viceversa.

    – Pinakin Kansara

    22 de febrero de 2018 a las 5:52

  • ¿No puedo usar el tipo Blob?

    –Jhon Fredy Trujillo Ortega

    7 de marzo de 2018 a las 17:03

  • @JhonFredyTrujilloOrtega, si no desea usar blob, primero comprima su imagen, luego conviértala a una cadena “base 64” y guárdela en db. No se recomienda almacenar “blob” o “base 64”. Lo mejor es almacenar el archivo localmente en el sistema de archivos y solo agregar una entrada de referencia en la base de datos. Sin embargo, depende de sus requisitos.

    – Pinakin Kansara

    13 de marzo de 2018 a las 5:10

  • Parece que esta solución no usa realmente BLOB. Esta solución hace que la aplicación mantenga la matriz de bytes completamente en la memoria, mientras que un verdadero usuario de BLOB, como en SQLite o MySQL, usaría InputStream para escribir y OutputStream para escribir. Entonces, si la matriz de bytes es demasiado grande, solo se mantiene un búfer en la memoria a la vez. Por cierto, BitmapFactory admite trabajar con InputStream. ¿A la base de datos de ROOM le falta esta función?

    – Elad Lavi

    28 de abril de 2018 a las 5:23

avatar de usuario de adithya reddy
adithya reddy

Como mencionó Pinakin, no se recomienda almacenar una imagen en la base de datos y la ruta del archivo sería mejor, pero si es necesario almacenar una imagen, sugeriría comprimir la imagen por debajo de 2 MB (aquí hay un ejemplo) para evitar romper la aplicación. Room admite BLOB para imagen. Clase de entidad en 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)
   }

Base de datos.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
   }
    

En DatabaseHelper algo como

  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 
     }

Corrígeme si estoy equivocado. Espero que esto ayude a alguien por ahí

  • Gracias por la explicación de cada línea y la solución fácil de entender de todo mi proyecto en Java.

    – Príncipe Kumar

    6 de noviembre de 2017 a las 9:51

Avatar de usuario de AdamHurwitz
adamhurwitz

Guarde la imagen como un archivo y guarde la ruta del archivo Uri en Room

Como se vio en CámaraX‘s captura de imagen caso de uso, cuando se toma una foto con éxito, el Uri de referencia de la ruta del archivo, savedUrise puede recuperar de forma segura.

Luego, el Uri se puede convertir en una cadena con savedUri.toString()y guardados en Room.

  • Es importante asegurarse de que la referencia del archivo de la sala también se actualice si el archivo se mueve o elimina.
  • Es posible que la cadena de imágenes guardada en Room deba convertirse nuevamente en un Uri para mostrarse con una biblioteca de imágenes como Glide con Uri.parse(someString).

En el ejemplo de CameraX, el Uri de una ruta de imagen se puede obtener de forma segura en onImageSaved.

  • Luego se guardaría en Room fuera del hilo principal usando Kotlin Coroutines o RxJava, preferiblemente en un ViewModel o en algún lugar que maneje la lógica comercial separada de la lógica de vista.

Primeros pasos con CameraX > 5. Implementar el caso de uso de ImageCapture

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)
           }
       })
}

Esta estrategia se describe en Guardar imagen en la base de datos de habitaciones en Reddit.

Almacenamiento en la nube

Crear un archivo para la imagen y guardar la ruta del archivo en Room cubre el almacenamiento local. Para garantizar que las imágenes se guarden en varios dispositivos o cuando se borre la caché de datos y los datos, una forma de Almacenamiento en la nube es necesario para cargar los archivos y descargarlos y sincronizarlos con el almacenamiento local.

¿Ha sido útil esta solución?