【问题标题】:How to compress an image file in Android?如何在 Android 中压缩图像文件?
【发布时间】:2017-03-28 21:55:47
【问题描述】:

所以对于每个图像,我都有它的路径(以字符串的形式)。我将该路径字符串转换为文件。然后我将该文件存储到 Firebase 存储中,但我的问题是查询时文件太大。所以我需要在上传 Firebase 存储之前对其进行压缩。我环顾四周,但从未找到关于如何做到这一点的明确解决方案。如果有人可以帮助我提供非常清晰和简单的解决方案,那就太好了。下面是我的代码。

for(String path : images)
{
    try {
    InputStream stream = new FileInputStream(new File(path));
    UploadTask uploadTask = imageStorage.putStream(stream);
    uploadTask.addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception exception) {
            // Handle unsuccessful uploads
            Log.d("myStorage","failure :(");
        }
    }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
        @Override
        public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
            // taskSnapshot.getMetadata() contains file metadata such as size, content-type, and download URL.
            UrI downloadUrl = taskSnapshot.getDownloadUrl();
            Log.d("myStorage","success!");
        }
    });
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    }
    countDB++;

}

【问题讨论】:

  • 您使用什么格式将图像存储在数据库中?如果可能,尝试使用 BLOB 格式存储图像。这将自动将图像大小减小到 KB。
  • 我现在使用jpeg。
  • 是的。但是db中的格式是什么?是 varchar 还是 int 还是 blob 等?

标签: android firebase compression firebase-storage image-compression


【解决方案1】:

我有一个用于 Firebase 存储的图像压缩自定义类,并且大小显着减小。

这个类可用于在发送到 Firebase 之前压缩位图和文件

public class ImageCompression {

public static Bitmap getThumbnail(Uri uri, Context context) throws FileNotFoundException, IOException {
    InputStream input = context.getContentResolver().openInputStream(uri);

    BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
    onlyBoundsOptions.inJustDecodeBounds = true;
    onlyBoundsOptions.inDither = true;//optional
    onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
    input.close();
    if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1))
        return null;

    int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;

    double ratio = (originalSize > 500) ? (originalSize / 500) : 1.0;

    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
    bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
    bitmapOptions.inDither = true;//optional
    bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    input = context.getContentResolver().openInputStream(uri);
    Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
    input.close();
    return bitmap;
}

private static int getPowerOfTwoForSampleRatio(double ratio) {
    int k = Integer.highestOneBit((int) Math.floor(ratio));
    if (k == 0) return 1;
    else return k;
}

public static File compressFile(File file, Context context) {
    try {

        // BitmapFactory options to downsize the image
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        o.inSampleSize = 6;
        // factor of downsizing the image

        FileInputStream inputStream = new FileInputStream(file);
        //Bitmap selectedBitmap = null;
        BitmapFactory.decodeStream(inputStream, null, o);
        inputStream.close();

        // The new size we want to scale to
        final int REQUIRED_SIZE = 75;

        // Find the correct scale value. It should be the power of 2.
        int scale = 1;
        while (o.outWidth / scale / 2 >= REQUIRED_SIZE &&
                o.outHeight / scale / 2 >= REQUIRED_SIZE) {
            scale *= 2;
        }

        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        inputStream = new FileInputStream(file);

        Bitmap selectedBitmap = BitmapFactory.decodeStream(inputStream, null, o2);
        inputStream.close();

        // here i override the original image file
        file.createNewFile();
        FileOutputStream outputStream = new FileOutputStream(file);

        selectedBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outputStream);

        return file;
    } catch (Exception e) {
        return null;
    }
}
} 



ImageCompression.compressFile(YourFile, this);
ImageCompression.getThumbnail(YourUri, this);

【讨论】:

  • 我会用什么代替“MyApplication”
  • 就是上下文,我用Application Context来做这个
  • 就我而言,如果我创建了一个上下文对象并将该上下文对象设置在构造函数中,那也可以吗?因为我不能像你一样访问应用程序上下文。
  • 但上下文类没有。 .getInstance().getContentResolver().openInputStream(uri); 所以当我这样做时context.getInstance().getContentResolver().openInputStream(uri); 它给了我一个红线错误
  • MyApplication 上仍有错误,我用什么替换它?
【解决方案2】:

这是你必须做的:

首先,将您的图像转换为位图:

Bitmap bitmap = ((BitmapDrawable) logo.getDrawable()).getBitmap();

将您的 imageView 替换为“徽标”。那么,

byte[] byteImage = encodeToBase64(bitmap);

public static byte[] encodeToBase64(Bitmap image) {
    ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();
    image.compress(Bitmap.CompressFormat.PNG, 25, byteArrayOS);
    byte[] byteArray = byteArrayOS.toByteArray();
    return byteArray;
}

您将以字节为单位将图像存储在“byteImage”变量中。数字“25”是您想要压缩它的百分比。在这段代码中,它将图像的大小减小到 25%。试试代码,让我知道:)

【讨论】:

  • 是的,它能够减小文件大小。如果我希望图像特别是 1024 x 1024,我该怎么做?
  • 您想在不降低图像分辨率的情况下减小文件大小?
  • @TheQ 我不知道这是否适合你,但尝试使用 tinyPNG API,这里是链接TinyPNG
【解决方案3】:

您可以使用以下代码获得所需大小的位图,

public static Bitmap decodeSampledBitmapFromArray(byte[] data, int reqWidth, int reqHeight) {
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeByteArray(data, 0, data.length, options);
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeByteArray(data, 0, data.length, options);
}

 public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;
    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

调用decodeSampledBitmapFromArray,使用位图字节数组和所需的宽度和高度。

以下是从位图获取字节数组的方法,

Bitmap bmp = Your_bitmap;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();

【讨论】:

    【解决方案4】:
    ImageView imageView = (ImageView)findViewById(R.id.imageView);
    Bitmap bitmap = ImageUtils.getInstant().getCompressedBitmap("Your_Image_Path_Here");
    imageView.setImageBitmap(bitmap);
    

    ImageUtils.java:

    public class ImageUtils {
    
        public static ImageUtils mInstant;
    
        public static ImageUtils getInstant(){
            if(mInstant==null){
                mInstant = new ImageUtils();
            }
            return mInstant;
        }
    
        public  Bitmap getCompressedBitmap(String imagePath) {
            float maxHeight = 1920.0f;
            float maxWidth = 1080.0f;
            Bitmap scaledBitmap = null;
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            Bitmap bmp = BitmapFactory.decodeFile(imagePath, options);
    
            int actualHeight = options.outHeight;
            int actualWidth = options.outWidth;
            float imgRatio = (float) actualWidth / (float) actualHeight;
            float maxRatio = maxWidth / maxHeight;
    
            if (actualHeight > maxHeight || actualWidth > maxWidth) {
                if (imgRatio < maxRatio) {
                    imgRatio = maxHeight / actualHeight;
                    actualWidth = (int) (imgRatio * actualWidth);
                    actualHeight = (int) maxHeight;
                } else if (imgRatio > maxRatio) {
                    imgRatio = maxWidth / actualWidth;
                    actualHeight = (int) (imgRatio * actualHeight);
                    actualWidth = (int) maxWidth;
                } else {
                    actualHeight = (int) maxHeight;
                    actualWidth = (int) maxWidth;
    
                }
            }
    
            options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight);
            options.inJustDecodeBounds = false;
            options.inDither = false;
            options.inPurgeable = true;
            options.inInputShareable = true;
            options.inTempStorage = new byte[16 * 1024];
    
            try {
                bmp = BitmapFactory.decodeFile(imagePath, options);
            } catch (OutOfMemoryError exception) {
                exception.printStackTrace();
    
            }
            try {
                scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888);
            } catch (OutOfMemoryError exception) {
                exception.printStackTrace();
            }
    
            float ratioX = actualWidth / (float) options.outWidth;
            float ratioY = actualHeight / (float) options.outHeight;
            float middleX = actualWidth / 2.0f;
            float middleY = actualHeight / 2.0f;
    
            Matrix scaleMatrix = new Matrix();
            scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
    
            Canvas canvas = new Canvas(scaledBitmap);
            canvas.setMatrix(scaleMatrix);
            canvas.drawBitmap(bmp, middleX - bmp.getWidth() / 2, middleY - bmp.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
    
            ExifInterface exif = null;
            try {
                exif = new ExifInterface(imagePath);
                int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
                Matrix matrix = new Matrix();
                if (orientation == 6) {
                    matrix.postRotate(90);
                } else if (orientation == 3) {
                    matrix.postRotate(180);
                } else if (orientation == 8) {
                    matrix.postRotate(270);
                }
                scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
            } catch (IOException e) {
                e.printStackTrace();
            }
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 85, out);
    
            byte[] byteArray = out.toByteArray();
    
            Bitmap updatedBitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
    
            return updatedBitmap;
        }
    
        private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;
    
            if (height > reqHeight || width > reqWidth) {
                final int heightRatio = Math.round((float) height / (float) reqHeight);
                final int widthRatio = Math.round((float) width / (float) reqWidth);
                inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
            }
            final float totalPixels = width * height;
            final float totalReqPixelsCap = reqWidth * reqHeight * 2;
    
            while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
                inSampleSize++;
            }
            return inSampleSize;
        }
    }
    

    这将压缩图像的大小,但不会调整它的大小。

    感谢这个答案Compress bitmap in android

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-22
      • 1970-01-01
      • 2011-09-21
      • 1970-01-01
      • 2015-02-19
      • 2017-12-11
      相关资源
      最近更新 更多