我一直在尝试将捕获的图像保存到房间数据库中,并稍后将其与其他详细信息一起上传到服务器。图像采集代码如下
val activityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data: Intent? = result.data
val uri: Uri? = if (data?.data != null){ data.data }else{ billUri }
if (uri != null) {
chequeBitmap = uriToBitmap(this, uri)
binding.chequeimage.setImageBitmap(chequeBitmap)
binding.addimage.visibility = View.GONE
binding.chequeimage.setOnClickListener {
// Clear the image and show the add image button
binding.chequeimage.setImageResource(R.drawable.image_upload)
binding.addimage.visibility = View.VISIBLE
}
} else {
Toast.makeText(this, "No Image Found", Toast.LENGTH_SHORT).show()
}
}
}
binding.addimage.setOnClickListener {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
intent.putExtra(MediaStore.EXTRA_MEDIA_TITLE, "Gallery")
val captureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmm_ss",
Locale.getDefault()).format(Date())
val storageDir: File? = getExternalFilesDir("DO_NOT_DELETE")
val photoFile: File? = File.createTempFile(
"Bill_${timeStamp}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
)
photoFile?.let { file ->
val billURI: Uri = FileProvider.getUriForFile(this, "$packageName.provider", file)
billUri = billURI
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, billURI)
}
当我将其上传到数据库时,它上传成功,但由于 sqlite 数据库中的行大小较大,应用程序无法读取它。然后应用程序永久崩溃,直到数据库被删除。
我正在尝试将图像作为 ByteArray 保存到房间数据库中
data class DepositData(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
var bill: String,
val payimage: ByteArray
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as DepositData
return payimage.contentEquals(other.payimage)
}
override fun hashCode(): Int {
return payimage.contentHashCode()
我希望图像大小应减少到 1.5 MB(最大)并保存到房间数据库中,而不会破坏图像质量或其清晰度。以前我曾经将图像捕获为缩略图,但它对于清晰的图像没有用处。加载时图像模糊。我不想将图像 URI 保存到房间中,因为用户可以删除它,并且应用程序可能因此崩溃。
要将其上传到数据库,我使用以下代码:
binding.save.setOnClickListener {
val payImage: ByteArray = bitmapToByteArray(chequeBitmap)!!
val data = DepositData(0, bill = bill, payimage = payImage)
collectionViewModel.insertRecord(data)
}
当我将其上传到数据库时,它上传成功,但由于 sqlite 数据库中的行大小较大,应用程序无法读取它
这不一定是真的,因为可以规避底层 CursorWindow 的限制。
可以通过从太大的 BLOB 中提取可管理数量的数据然后进行组合来减少数据量。
就图像/媒体而言,建议的方法是存储一种然后提取媒体的方法,这很可能位于应用程序的数据内。
演示
以下是一个简单的演示,在资产文件夹中有一个较大的图像(3MB),然后通过两种方法将其存储在数据库中。
@Entity
带注释的对象(SQLite 表),该对象具有id 和 image 字段 (BLOB)
@Entity
带注释的对象(SQLite表);
@Entity
带注释的对象是:-
@Entity
data class AltImage(
@PrimaryKey
val id: Long?=null,
val image: ByteArray
)
@Entity
data class ImageParent(
@PrimaryKey
val id: Long?=null,
val timestamp: Long=System.currentTimeMillis(),
@ColumnInfo(index = true)
val imageName: String
)
@Entity
data class DataChunk(
@PrimaryKey
val id: Long?=null,
@ColumnInfo(index = true)
val mapTo_ImageParent: Long,
val chunk: ByteArray
)
为了促进第二种(块)方法,还有一个 FullImage 对象,可以帮助使用 @Embedded
和
@Relation
注释提取相关数据。该类还有几个函数可以帮助处理块(拆分和组合):-
data class FullImage(
@Embedded
val imageParent: ImageParent,
@Relation(
entity = DataChunk::class,
parentColumn = "id",
entityColumn = "mapTo_ImageParent"
)
val dataChunkList: List<DataChunk>
) {
companion object {
fun getArrayOfChunks(fullImage: ByteArray): ArrayList<ByteArray> {
val rv: ArrayList<ByteArray> = ArrayList()
var offset=0
while(offset < fullImage.size) {
Log.d("APPINFO","getArrayOfChunks Loop, Offset is ${offset} FullImageSize = ${fullImage.size}")
if (fullImage.size - (offset + CHUNK_SIZE) >0) {
rv.add(fullImage.copyOfRange(offset, offset + CHUNK_SIZE))
} else {
rv.add(fullImage.copyOfRange(offset,offset + (fullImage.size-offset)))
}
offset+= CHUNK_SIZE
}
return rv
}
}
fun getCombinedChunks(): ByteArray {
val rv: ArrayList<Byte> = ArrayList()
for (c in dataChunkList) {
for (b in c.chunk)
rv.add(b)
}
return rv.toByteArray()
}
}
至于访问数据库,单个 @Dao
带注释的AllDAOs 接口是:-
@Dao
interface AllDAOs {
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(imageParent: ImageParent): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(dataChunk: DataChunk): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(altImage: AltImage)
@Query("SELECT * FROM imageparent")
fun getAllFullImages(): List<FullImage>
}
将它们放在一起,然后是一个 @Database
带注释的类:-
@Database(entities = [ImageParent::class,DataChunk::class,AltImage::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDAOs(): AllDAOs
companion object {
private var instance: TheDatabase?=null
fun getInstance(context: Context): TheDatabase {
if (instance==null) {
instance=Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
.allowMainThreadQueries() /* for brevity of the DEMO */
.build()
}
return instance as TheDatabase
}
}
}
实际使用上面的活动代码:-
const val CHUNK_SIZE=1024 * 4
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDAOs
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val theImage = this.assets.open("theimage.JPG").readBytes()
Log.d("APPINFO","Image length=${theImage.size}")
db = TheDatabase.getInstance(this)
dao = db.getAllDAOs()
val altImageId = dao.insert(AltImage(null,theImage.copyOf()))
val ipId = ImageParent(id =dao.insert(ImageParent(imageName = "TheImage")), imageName = "TheImage")
for(dc in FullImage.getArrayOfChunks(theImage)) {
dao.insert(DataChunk(null, mapTo_ImageParent = ipId.id!!,dc))
}
val extracted = dao.getAllFullImages()
for (e in extracted) {
Log.d("APPINFO"," Image has ${e.dataChunkList.size} chunks of (${CHUNK_SIZE}) (note last chuck may be smaller) bytearray size is ${e.getCombinedChunks().size}")
}
}
}
运行上述命令(从全新安装)时,日志中将显示以下结果:-
2024-05-07 10:28:01.983 D/APPINFO: Image length=3422566
2024-05-07 10:28:02.137 D/APPINFO: getArrayOfChunks Loop, Offset is 0 FullImageSize = 3422566
2024-05-07 10:28:02.153 D/APPINFO: getArrayOfChunks Loop, Offset is 4096 FullImageSize = 3422566
2024-05-07 10:28:02.153 D/APPINFO: getArrayOfChunks Loop, Offset is 8192 FullImageSize = 3422566
2024-05-07 10:28:02.153 D/APPINFO: getArrayOfChunks Loop, Offset is 12288 FullImageSize = 3422566
2024-05-07 10:28:02.153 D/APPINFO: getArrayOfChunks Loop, Offset is 16384 FullImageSize = 3422566
2024-05-07 10:28:02.153 D/APPINFO: getArrayOfChunks Loop, Offset is 20480 FullImageSize = 3422566
2024-05-07 10:28:02.153 D/APPINFO: getArrayOfChunks Loop, Offset is 24576 FullImageSize = 3422566
2024-05-07 10:28:02.153 D/APPINFO: getArrayOfChunks Loop, Offset is 28672 FullImageSize = 3422566
2024-05-07 10:28:02.153 D/APPINFO: getArrayOfChunks Loop, Offset is 32768 FullImageSize = 3422566
....
2024-05-07 10:28:02.218 D/APPINFO: getArrayOfChunks Loop, Offset is 3403776 FullImageSize = 3422566
2024-05-07 10:28:02.218 D/APPINFO: getArrayOfChunks Loop, Offset is 3407872 FullImageSize = 3422566
2024-05-07 10:28:02.218 D/APPINFO: getArrayOfChunks Loop, Offset is 3411968 FullImageSize = 3422566
2024-05-07 10:28:02.218 D/APPINFO: getArrayOfChunks Loop, Offset is 3416064 FullImageSize = 3422566
2024-05-07 10:28:02.218 D/APPINFO: getArrayOfChunks Loop, Offset is 3420160 FullImageSize = 3422566
2024-05-07 10:28:03.997 W/CursorWindow: Window is full: requested allocation 4096 bytes, free space 3884 bytes, window size 2097152 bytes
2024-05-07 10:28:04.538 D/APPINFO: Image has 836 chunks of (4096) (note last chunk may be smaller) bytearray size is 3422566
和:-
通过:-