Как сжать файл изображения в Android?

Итак, для каждого изображения у меня есть свой путь (в виде строки). И я конвертирую эту строку пути в файл. Затем я сохраняю этот файл в хранилище 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++;

}

person TheQ    schedule 15.11.2016    source источник
comment
какой формат вы используете для хранения изображения в БД?. Если возможно, попробуйте использовать формат BLOB для хранения изображения. это автоматически уменьшит размер изображения до КБ.   -  person Priya Jagtap    schedule 15.11.2016
comment
я использую jpeg в данный момент.   -  person TheQ    schedule 15.11.2016
comment
да. но что такое формат в БД? это varchar или int или blob и т. д.?   -  person Priya Jagtap    schedule 15.11.2016


Ответы (4)


У меня есть собственный класс для сжатия изображений, который я использовал для хранилища 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);
person Veeresh Charantimath    schedule 15.11.2016
comment
что бы я поставил вместо MyApplication - person TheQ; 15.11.2016
comment
Это контекст, я использую контекст приложения для этого - person Veeresh Charantimath; 15.11.2016
comment
в моем случае, если бы я создал объект контекста и установил этот объект контекста в конструкторе, это тоже сработало бы? потому что я не могу получить доступ к контексту приложения так же, как вы. - person TheQ; 15.11.2016
comment
но класса контекста нет. .getInstance().getContentResolver().openInputStream(uri); Итак, когда я делаю context.getInstance().getContentResolver().openInputStream(uri);, появляется ошибка красной строки - person TheQ; 15.11.2016
comment
На MyApplication все еще есть ошибка, на что ее заменить? - person TheQ; 15.11.2016
comment
Давайте продолжим это обсуждение в чате. - person Veeresh Charantimath; 15.11.2016

Вот что вам нужно сделать:

Сначала преобразуйте изображение в растровое изображение:

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%. Попробуйте код и дайте мне знать :)

person Abhi    schedule 15.11.2016
comment
Да, удалось уменьшить размер файла. Если я хочу, чтобы изображение было особенно 1024 x 1024, как мне это сделать? - person TheQ; 15.11.2016
comment
Вы хотите уменьшить размер файла, не уменьшая разрешение изображения? - person Abhi; 15.11.2016
comment
@TheQ Я не знаю, сработает ли это для вас, но попробуйте использовать API tinyPNG, вот ссылка TinyPNG - person Abhi; 15.11.2016

Вы можете получить растровое изображение требуемого размера, используя следующий код:

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();
person rks    schedule 15.11.2016

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;
    }
}

Это сожмет размер изображения, но не изменит его размер.

Благодаря этому ответу Сжать растровое изображение в Android

person rana_sadam    schedule 15.11.2016