如何在上传至Firebase前压缩图片?附Android上传代码求助
问题描述
我正在开发一款支持用户发布图片的社交网络应用,用户上传的图片体积过大,使用Picasso加载时耗时过长。请问是否有办法在上传至Firebase前对图片进行低质量损耗的压缩,以提升图片的加载效率?
以下是我的图片上传代码:
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if(requestCode == Gallery_Pick && resultCode == RESULT_OK && data != null){ ImageUri = data.getData(); SelectPostImage.setImageURI(ImageUri); } super.onActivityResult(requestCode, resultCode, data); } final StorageReference filePath = PostImagesRef.child("Post Images").child(ImageUri.getLastPathSegment() + postRandomName + ".jpg"); final UploadTask uploadTask = filePath.putFile(ImageUri); uploadTask.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { String message = e.toString(); Toast.makeText(PostActivity.this, "Some Error Occured"+message, Toast.LENGTH_SHORT).show(); loadingBar.dismiss(); } }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { Toast.makeText(PostActivity.this, "Image Uploaded Successfully", Toast.LENGTH_SHORT).show(); Task<Uri> urlTask = uploadTask.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() { @Override public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception { if(!task.isSuccessful()){ throw task.getException(); } downloadImageUrl = filePath.getDownloadUrl().toString(); return filePath.getDownloadUrl(); } }).addOnCompleteListener(new OnCompleteListener<Uri>() { @Override public void onComplete(@NonNull Task<Uri> task) { if(task.isSuccessful()){ downloadImageUrl = task.getResult().toString(); Toast.makeText(PostActivity.this, "Saved To Database Successfully", Toast.LENGTH_SHORT).show(); SavingPostInformationToDatabase(); } } }); } });
解决方案
当然可以!针对你遇到的图片体积大导致Picasso加载慢的问题,在上传到Firebase之前做低损耗压缩是非常有效的解决方案,我给你整理了两种实用的方案,结合你的现有代码来调整:
方案一:原生API质量压缩(低视觉损耗)
质量压缩不会改变图片的像素尺寸,只是降低文件的存储体积,视觉上几乎看不出差异,适合大多数社交场景。你需要先将选中的Uri转换成压缩后的本地文件,再上传这个文件到Firebase。
步骤1:添加压缩工具方法
在你的PostActivity中新增一个压缩图片的工具方法:
private File compressImage(Uri imageUri) throws IOException { // 从Uri获取原始Bitmap Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri); // 创建临时文件存储压缩后的图片(存在应用缓存目录,不会占用用户外部存储空间) File tempFile = new File(getExternalCacheDir(), "compressed_post_image.jpg"); // 配置压缩参数:JPEG格式,质量设为80(范围0-100,值越高质量越好、体积越大) ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); // 将压缩后的字节写入临时文件 FileOutputStream fos = new FileOutputStream(tempFile); fos.write(baos.toByteArray()); fos.flush(); fos.close(); // 回收Bitmap避免内存泄漏 if (!bitmap.isRecycled()) { bitmap.recycle(); } return tempFile; }
步骤2:修改上传逻辑
把原来直接上传原始Uri的代码,改成上传压缩后的文件,调整后的代码如下:
// 先执行图片压缩 try { File compressedImageFile = compressImage(ImageUri); Uri compressedImageUri = Uri.fromFile(compressedImageFile); // 保持原有存储路径逻辑不变 final StorageReference filePath = PostImagesRef.child("Post Images").child(ImageUri.getLastPathSegment() + postRandomName + ".jpg"); final UploadTask uploadTask = filePath.putFile(compressedImageUri); // 后续的上传监听逻辑完全复用你原来的代码即可 uploadTask.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { String message = e.toString(); Toast.makeText(PostActivity.this, "Some Error Occured"+message, Toast.LENGTH_SHORT).show(); loadingBar.dismiss(); // 上传失败也记得删除临时文件 compressedImageFile.delete(); } }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { Toast.makeText(PostActivity.this, "Image Uploaded Successfully", Toast.LENGTH_SHORT).show(); Task<Uri> urlTask = uploadTask.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() { @Override public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception { if(!task.isSuccessful()){ throw task.getException(); } downloadImageUrl = filePath.getDownloadUrl().toString(); return filePath.getDownloadUrl(); } }).addOnCompleteListener(new OnCompleteListener<Uri>() { @Override public void onComplete(@NonNull Task<Uri> task) { if(task.isSuccessful()){ downloadImageUrl = task.getResult().toString(); Toast.makeText(PostActivity.this, "Saved To Database Successfully", Toast.LENGTH_SHORT).show(); SavingPostInformationToDatabase(); // 上传完成后删除临时文件,释放空间 compressedImageFile.delete(); } } }); } }); } catch (IOException e) { e.printStackTrace(); Toast.makeText(PostActivity.this, "Image compression failed", Toast.LENGTH_SHORT).show(); }
方案二:质量+尺寸双重压缩(进一步减小体积)
如果质量压缩后体积还是超出预期,可以同时进行尺寸压缩——将图片的最长边限制在1080px(适配大多数手机屏幕),这样既能大幅减小体积,又能保证显示效果。
修改后的压缩工具方法:
private File compressImage(Uri imageUri) throws IOException { // 第一步:获取原始图片的尺寸(不加载Bitmap,避免内存占用) BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; InputStream is = getContentResolver().openInputStream(imageUri); BitmapFactory.decodeStream(is, null, options); is.close(); // 计算采样率,将最长边限制在1080px int maxTargetSize = 1080; int originalWidth = options.outWidth; int originalHeight = options.outHeight; int inSampleSize = 1; if (originalHeight > maxTargetSize || originalWidth > maxTargetSize) { final int halfHeight = originalHeight / 2; final int halfWidth = originalWidth / 2; // 逐步计算合适的采样率,保证缩小后的图片最长边不超过目标值 while ((halfHeight / inSampleSize) >= maxTargetSize && (halfWidth / inSampleSize) >= maxTargetSize) { inSampleSize *= 2; } } // 第二步:加载缩小后的Bitmap options.inJustDecodeBounds = false; options.inSampleSize = inSampleSize; is = getContentResolver().openInputStream(imageUri); Bitmap bitmap = BitmapFactory.decodeStream(is, null, options); is.close(); // 第三步:再执行质量压缩 File tempFile = new File(getExternalCacheDir(), "compressed_post_image.jpg"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos); FileOutputStream fos = new FileOutputStream(tempFile); fos.write(baos.toByteArray()); fos.flush(); fos.close(); // 回收Bitmap if (!bitmap.isRecycled()) { bitmap.recycle(); } return tempFile; }
额外小贴士
- 临时文件记得在上传完成/失败后删除,避免占用应用缓存空间
- 如果你的应用需要处理大量不同场景的图片,可以考虑使用成熟的第三方压缩库(比如Luban),它能自动根据图片内容优化压缩策略,减少手动调整的成本
内容的提问来源于stack exchange,提问作者Ankur Singhal




