如何压缩图片并保存至Firebase Cloud Storage?Android开发求助
Android图片压缩至100KB左右并上传Firebase解决方案
问题修正与核心思路
你的现有代码直接上传选中的原图,未做压缩处理,还存在变量误用(回调里的result才是选中图片的Uri,代码里错用了mImgUri)。下面是结合尺寸压缩+质量压缩实现目标的完整方案,既能把图片压到100KB左右,又能尽量保留画质。
完整代码实现
1. 图片压缩工具方法
import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; public class ImageCompressor { // 目标压缩大小(100KB) private static final long TARGET_SIZE = 100 * 1024; /** * 将Uri转为压缩后的临时文件 */ public static File compressImage(Context context, Uri imageUri) throws IOException { // 1. 先获取原图尺寸,计算缩小比例 InputStream inputStream = context.getContentResolver().openInputStream(imageUri); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(inputStream, null, options); inputStream.close(); // 把大图先缩到1080P以内,避免直接压缩导致画质崩 options.inSampleSize = calculateSampleSize(options, 1080, 1920); options.inJustDecodeBounds = false; inputStream = context.getContentResolver().openInputStream(imageUri); Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options); inputStream.close(); // 2. 循环调整质量压缩,直到接近目标大小 ByteArrayOutputStream baos = new ByteArrayOutputStream(); int quality = 100; bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos); while (baos.toByteArray().length > TARGET_SIZE && quality > 10) { baos.reset(); quality -= 10; bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos); } // 3. 写入临时文件供上传 File tempFile = File.createTempFile("compressed_img", ".jpg", context.getCacheDir()); FileOutputStream fos = new FileOutputStream(tempFile); fos.write(baos.toByteArray()); fos.flush(); fos.close(); bitmap.recycle(); // 释放Bitmap内存,防止OOM return tempFile; } /** * 计算图片缩小比例 */ private static int calculateSampleSize(BitmapFactory.Options options, int maxWidth, int maxHeight) { int width = options.outWidth; int height = options.outHeight; int sampleSize = 1; if (height > maxHeight || width > maxWidth) { int halfHeight = height / 2; int halfWidth = width / 2; while ((halfHeight / sampleSize) >= maxHeight && (halfWidth / sampleSize) >= maxWidth) { sampleSize *= 2; } } return sampleSize; } }
2. 修改后的图片选择与上传代码
// 注册图片选择启动器 ActivityResultLauncher<String> launcher = registerForActivityResult( new ActivityResultContracts.GetContent(), result -> { if (result == null) return; // 压缩是耗时操作,必须放子线程 new Thread(() -> { try { // 压缩图片到临时文件 File compressedFile = ImageCompressor.compressImage(getApplicationContext(), result); // 上传压缩后的文件到Firebase StorageReference reference = FirebaseStorage.getInstance() .getReference("MESSAGE") .child(mMsgPath) .child(System.currentTimeMillis() + ".jpg"); // 用时间戳做文件名,避免重复覆盖 reference.putFile(Uri.fromFile(compressedFile)) .addOnSuccessListener(taskSnapshot -> { reference.getDownloadUrl().addOnSuccessListener(uri -> { String url = uri.toString(); // 这里可以把url存入聊天消息列表 }); }) .addOnFailureListener(e -> { // 处理上传失败逻辑 }); } catch (IOException e) { e.printStackTrace(); } }).start(); }); // 启动图片选择 launcher.launch("image/*");
新手必看注意事项
- 压缩必须放子线程,否则会导致App卡顿甚至ANR
- 用时间戳做文件名,避免上传时覆盖同路径下的旧图片
- 压缩后记得调用
bitmap.recycle()释放内存,防止内存溢出 - 如果需要处理拍照后图片旋转的问题,可以在压缩工具类里补充Exif信息读取逻辑(新手可后期再优化)
内容的提问来源于stack exchange,提问作者Mukesh Roy
相关产品推荐
相关产品推荐

