如何解决向Firestore上传Bitmap转字符串图片的画质损失问题
图片转Bitmap并编码上传Firestore后画质不佳,如何修复?
我已经实现了将图片转为Bitmap再编码为字符串上传至Firestore的功能,但上传后图片画质很差。以下是我的相关代码:
选取图片并转换为Bitmap的代码
val pickImage: ActivityResultLauncher<Intent> = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { if (it.resultCode == Activity.RESULT_OK) { if (it.data != null) { val imageUri: Uri? = it.data!! try { val inputStream: InputStream? = imageUri?.let { it1 -> APP_ACTIVITY.contentResolver.openInputStream( it1 ) } val options = BitmapFactory.Options() options.inPreferredConfig = Bitmap.Config.ARGB_8888 options.inDither = false options.inScaled = false options.inPremultiplied = false options.inSampleSize = 1 options.inPreferQualityOverSpeed = true val bitmap = BitmapFactory.decodeStream(inputStream,null,options) var user_id = preferenceManager.getString(KEY_USER_ID)! mViewModel.sendMessageAsImage(bitmap!!,receiverUser.uid,user_id){ mBinding.typeMessageField.setText(null) } } catch (e: FileNotFoundException) { e.printStackTrace() } } } }
将Bitmap编码为字符串的函数
private fun encodedImage(bitmap: Bitmap): String? { val previewWidth = 500 val previewHeight = bitmap.height * previewWidth / bitmap.width val previewBitmap = Bitmap.createScaledBitmap(bitmap, previewWidth, previewHeight, false) val byteArrayOutputStream = ByteArrayOutputStream() previewBitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) val bytes = byteArrayOutputStream.toByteArray() return Base64.encodeToString(bytes, Base64.DEFAULT) }
将字符串解码回Bitmap的函数
private fun getBitmapFromEncodedString(encodedImage:String): Bitmap { val bytes = Base64.decode(encodedImage, Base64.DEFAULT) return BitmapFactory.decodeByteArray(bytes,0,bytes.size) }
修复方案
1. 移除强制缩小图片的操作
当前encodedImage函数里强制将图片缩放到500px宽度,这是画质下降的核心原因。如果不需要限制图片宽度,直接使用原Bitmap进行编码:
private fun encodedImage(bitmap: Bitmap): String? { val byteArrayOutputStream = ByteArrayOutputStream() // 保留原尺寸进行压缩 bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) val bytes = byteArrayOutputStream.toByteArray() return Base64.encodeToString(bytes, Base64.DEFAULT) }
如果需要限制最大尺寸,改用高质量缩放逻辑避免画质损失:
private fun encodedImage(bitmap: Bitmap): String? { val maxWidth = 1000 // 根据需求调整最大宽度 var targetBitmap = bitmap if (bitmap.width > maxWidth) { val scaleFactor = maxWidth.toFloat() / bitmap.width val targetHeight = (bitmap.height * scaleFactor).toInt() // 使用双三次插值提升缩放画质 val paint = Paint().apply { isFilterBitmap = true interpolation = Paint.INTERPOLATION_BICUBIC } targetBitmap = Bitmap.createBitmap(maxWidth, targetHeight, Bitmap.Config.ARGB_8888) val canvas = Canvas(targetBitmap) canvas.drawBitmap(bitmap, Rect(0, 0, bitmap.width, bitmap.height), Rect(0, 0, maxWidth, targetHeight), paint) } val byteArrayOutputStream = ByteArrayOutputStream() targetBitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) val bytes = byteArrayOutputStream.toByteArray() // 回收临时Bitmap避免内存泄漏 if (targetBitmap != bitmap) targetBitmap.recycle() return Base64.encodeToString(bytes, Base64.DEFAULT) }
2. 优化Bitmap解码逻辑
使用ContentResolver.openFileDescriptor替代openInputStream,能更高效、准确地解码原图,避免流读取导致的解码损失:
val imageUri: Uri? = it.data!! try { APP_ACTIVITY.contentResolver.openFileDescriptor(imageUri, "r")?.use { fd -> val options = BitmapFactory.Options() options.inPreferredConfig = Bitmap.Config.ARGB_8888 options.inDither = false options.inScaled = false options.inPremultiplied = false options.inSampleSize = 1 options.inPreferQualityOverSpeed = true val bitmap = BitmapFactory.decodeFileDescriptor(fd.fileDescriptor, null, options) // 后续逻辑不变 } } catch (e: IOException) { e.printStackTrace() }
3. 更换更优的压缩格式
JPEG即使100%质量仍属于有损压缩,若需要无损画质可改用PNG;或者使用WebP格式,在相同体积下能提供更好的画质:
// 无损WebP压缩(体积比PNG小) bitmap.compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, byteArrayOutputStream) // 有损WebP压缩,可调整质量参数(如90)平衡画质和体积 bitmap.compress(Bitmap.CompressFormat.WEBP, 90, byteArrayOutputStream)
4. 解码时保留画质
解码Base64字符串时,添加配置确保Bitmap解码质量:
private fun getBitmapFromEncodedString(encodedImage: String): Bitmap { val bytes = Base64.decode(encodedImage, Base64.DEFAULT) val options = BitmapFactory.Options() options.inPreferredConfig = Bitmap.Config.ARGB_8888 options.inPreferQualityOverSpeed = true return BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options) }
内容的提问来源于stack exchange,提问作者Danil
相关产品推荐
相关产品推荐

