You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将拍照/相册图片压缩至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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 20:00:34