如何在房间持久性库中插入图像?

Posted

技术标签:

【中文标题】如何在房间持久性库中插入图像?【英文标题】:How insert image in room persistence library? 【发布时间】:2018-03-02 10:15:55 【问题描述】:

我正在为我的 android 应用程序使用房间持久性库,现在我必须在我的数据库中插入图像。我成功地为原始数据类型定义了@Entity。并且还通过转换器类,我存储了所有对象、日期、时间。现在我必须存储图像。我无法理解我们如何定义列信息和实体,以及我们如何插入该数据以及如何从表中读取数据。

插入单行的最大数据大小是多少? Android SQLite 中一个字段中数据的最大和最小大小是多少?

【问题讨论】:

您可以使用 blob 将图像存储在 Room 中。 关注developer.android.com/topic/libraries/architecture/room.html @CommonsWare 如果您不建议将图像存储在数据库中,您有什么建议?将图像存储在外部存储中? @Aniruddha:使用internal storage还是external storage取决于用户是否需要独立访问图像。 @CommonsWare 如果您能解释为什么不建议将图像存储在数据库中,那将是最好的学习目的。 【参考方案1】:

通常不建议将图像数据存储到数据库中。 但是,如果您的项目需要它,那么您可以这样做。

图像数据通常使用BLOB数据类型存储到db中,Room也提供对BLOB数据类型的支持Documentation

您可以如下所述声明您的实体类来存储图像数据。

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

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

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

【讨论】:

感谢它的工作。我需要添加将原始图像转换为 byte[] 和 byte[] 为原始图像的编码器和解码器 @PrinceKumar 是的,您需要编码器和解码器将位图转换为字节 [],反之亦然。 我不能使用 Blob 类型? @JhonFredyTrujilloOrtega,如果您不想使用 blob,请先压缩图像,然后将其转换为“base 64”字符串并将其存储在数据库中。不建议存储“blob”或“base 64”。最好将文件本地存储到文件系统中,并且只在 db 中添加引用条目。不过可以满足你的要求。 这个解决方案似乎没有真正使用 BLOB。该解决方案使应用程序将字节数组完全保存在内存中,而真正的 BLOB 用户(例如在 SQLite 或 mysql 中)将使用 InputStream 进行写入,OutputStream 进行写入。因此,如果字节数组太大,一次只能在内存中保存一个缓冲区。顺便说一句,BitmapFactory 支持使用 InputStream。 ROOM 数据库是否缺少此功能?【参考方案2】:

正如 Pinakin 所说,不建议将图像存储到数据库中,文件路径会更好,但如果需要存储图像,我建议将图像压缩到 2 MB 以下 (here is an example) 以避免破坏应用程序. Room 支持图像的 BLOB。 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
   
    

在 DatabaseHelper 中类似于

  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 
     

如果我错了,请纠正我。希望这可以帮助那里的人

【讨论】:

感谢每一行的解释和易于理解的解决方案,我在 java 中的整个项目。【参考方案3】:

将图片保存为文件并将文件路径Uri保存到Room

CameraXimage capture用例中所见,当成功拍摄照片时,文件路径引用Uri,savedUri,可以安全取回。

然后,可以将 Uri 转换为带有savedUri.toString() 的字符串,并保存到 Room。

如果文件被移动或删除,请务必确保 Room 文件引用也得到更新。 保存在 Room 中的图像字符串可能需要转换回 Uri 才能与图像库一起显示,例如带有 Uri.parse(someString) 的 Glide。

在 CameraX 示例中,可以在 onImageSaved 中安全地获取图像路径的 Uri。

然后将使用 Kotlin Coroutines 或 RxJava 将其保存到主线程之外的 Room 中,最好保存在 ViewModel 或处理与视图逻辑分开的业务逻辑的地方。

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

Reddit 上的Saving image in Room database 中概述了此策略。

云存储

为图像创建文件并将文件路径保存在 Room 中涵盖了本地存储。为了确保图像在多个设备上保存,或者当数据缓存和数据被清除时,需要一个 Cloud Storage 的形式将文件上传到本地存储并下载并与本地存储同步.

【讨论】:

以上是关于如何在房间持久性库中插入图像?的主要内容,如果未能解决你的问题,请参考以下文章

插入房间后的行

如何使主键作为房间持久性库的自动增量

具有新线程和数据绑定问题的房间持久库

房间持久性库。删除所有

Android 房间持久库 - TypeConverter 错误错误:无法弄清楚如何将字段保存到数据库”

iPhone UIImage - 数据持久性