You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在上传至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

火山引擎 最新活动