如何将拍照/相册图片压缩至1.5MB内并存入Room数据库
解决方案:将图片压缩至1.5MB并存入Room数据库
核心思路
通过迭代式质量压缩结合高效的图片格式(WebP),在确保清晰度的前提下将图片大小控制在1.5MB以内,同时避免存储URI带来的风险。
1. 实现智能图片压缩函数
创建一个函数,自动调整压缩质量,直到图片大小不超过1.5MB。优先使用WebP格式(比JPEG压缩率更高、质量更好),仅在必要时缩小图片尺寸:
import android.graphics.Bitmap import android.graphics.BitmapFactory import android.os.Build import kotlin.math.sqrt fun compressBitmapToMaxSize(bitmap: Bitmap, maxSizeInBytes: Int = 1572864): ByteArray? { // 根据系统版本选择最优压缩格式 val compressFormat = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { Bitmap.CompressFormat.WEBP_LOSSLESS // 安卓11+支持无损WebP } else { Bitmap.CompressFormat.WEBP // 旧版本使用有损WebP(质量损失极低) } var quality = 100 var outputStream: ByteArrayOutputStream var compressedBytes: ByteArray // 迭代调整压缩质量,直到满足大小限制 do { outputStream = ByteArrayOutputStream() bitmap.compress(compressFormat, quality, outputStream) compressedBytes = outputStream.toByteArray() quality -= 5 // 每次降低5%质量,步长可按需调整 } while (compressedBytes.size > maxSizeInBytes && quality > 10) // 最低保留10%质量 // 如果降到10%仍超大小,再尝试缩小图片尺寸(最后手段) if (compressedBytes.size > maxSizeInBytes) { val scaleFactor = sqrt(maxSizeInBytes.toDouble() / compressedBytes.size.toDouble()) val scaledBitmap = Bitmap.createScaledBitmap( bitmap, (bitmap.width * scaleFactor).toInt(), (bitmap.height * scaleFactor).toInt(), true ) outputStream = ByteArrayOutputStream() scaledBitmap.compress(compressFormat, 80, outputStream) compressedBytes = outputStream.toByteArray() scaledBitmap.recycle() } bitmap.recycle() // 释放原始Bitmap内存 return compressedBytes }
2. 优化URI转Bitmap的内存效率
避免直接加载全分辨率图片导致内存溢出,先计算采样率再解码:
import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri fun uriToBitmap(context: Context, uri: Uri): Bitmap? { val options = BitmapFactory.Options() options.inJustDecodeBounds = true var inputStream = context.contentResolver.openInputStream(uri) BitmapFactory.decodeStream(inputStream, null, options) inputStream?.close() // 计算采样率,限制图片最大宽高为1024px(可按需调整) val maxDimension = 1024 var sampleSize = 1 while (options.outWidth / sampleSize > maxDimension || options.outHeight / sampleSize > maxDimension) { sampleSize *= 2 } options.inJustDecodeBounds = false options.inSampleSize = sampleSize inputStream = context.contentResolver.openInputStream(uri) val bitmap = BitmapFactory.decodeStream(inputStream, null, options) inputStream?.close() return bitmap }
3. 修改存储逻辑
在保存时使用压缩后的ByteArray替代原始图片数据:
binding.save.setOnClickListener { chequeBitmap?.let { bitmap -> val compressedImage = compressBitmapToMaxSize(bitmap) ?: run { Toast.makeText(this, "图片压缩失败", Toast.LENGTH_SHORT).show() return@setOnClickListener } val data = DepositData(0, bill = bill, payimage = compressedImage) collectionViewModel.insertRecord(data) Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show() } ?: Toast.makeText(this, "请先选择图片", Toast.LENGTH_SHORT).show() }
4. 从Room读取并显示图片
将存储的ByteArray解码为Bitmap显示:
fun byteArrayToBitmap(byteArray: ByteArray): Bitmap? { return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size) } // 使用示例:从DepositData中读取图片并显示 val bitmap = byteArrayToBitmap(depositData.payimage) bitmap?.let { binding.chequeimage.setImageBitmap(it) }
关键优势
- 质量保障:优先通过调整压缩质量而非缩小尺寸控制大小,最大程度保留清晰度
- 高效压缩:WebP格式比JPEG节省约25%-35%的空间,相同质量下体积更小
- 内存优化:解码时通过采样率限制Bitmap大小,避免内存溢出
- 数据安全:存储图片二进制数据,无需依赖外部文件,不会因用户删除原图导致崩溃
内容的提问来源于stack exchange,提问作者Shivam Verma
相关产品推荐
相关产品推荐

