5

I have a function to send a file (picture from camera or gallery) to a WebService. I would like to reduce the image size of fileUri before post (50% per example). The file is a gallery or camera image.

This is my postFile function :

public static void postFile(Context context, String url, String fileUri, AsyncHttpResponseHandler responseHandler) {
    if (myCookieStore == null)
    {
        myCookieStore = new PersistentCookieStore(context);
        client.setCookieStore(myCookieStore);
    }

    File myFile = new File(Uri.parse(fileUri).getPath());

    RequestParams params = new RequestParams();
    try {
        params.put("profile_picture", myFile);
    } catch(FileNotFoundException e) {
        Log.d("error", "error catch");
    }
    Log.d("absolute url", "" + "*" + getAbsoluteUrl(url) + "*");
    client.post(context, getAbsoluteUrl(url), params, responseHandler);
}

How can I do that ?

Jéwôm'
  • 3,753
  • 5
  • 40
  • 73
  • It is really unclear what you are asking. From wording, you either want to reduce the image size, or just the path/name of the file. Not sure which – IAmGroot Apr 11 '17 at 10:36
  • I want to reduce the image size (I edit my question). – Jéwôm' Apr 11 '17 at 10:37
  • see this http://stackoverflow.com/questions/18573774/how-to-reduce-an-image-file-size-before-uploading-to-a-server – Rajesh Apr 15 '17 at 12:53
  • There are several API's for image shrinking services you can use – Zoe Apr 17 '17 at 14:00

6 Answers6

4

There is this library, that can compress your images to kb from mb, it is very powerful i have used it alot of times, it works file uploads are superfast. link

Snippet : compressedImageFile = Compressor.getDefault(this).compressToFile(actualImageFile);

It internally uses google webp format, WebP is a modern image format that provides superior lossless and lossy compression for images on the web. Using WebP, webmasters and web developers can create smaller, richer images that make the web faster.

The library is great at size compression, it does a really good job, at large files that was based on my observations, like 2mb up, however there are some memory leaks that you need to address, i solved mine by using leak canary , though every developer should always use it. Overall it is awesome fork it and use as please.

Remario
  • 3,813
  • 2
  • 18
  • 25
  • you can also extend the functionality if you please, you can also use reactive extensions to let be more functional – Remario Apr 13 '17 at 10:46
  • i do not recommend simply compressing your fie with the bitmap compression, for one quality is loss, and also it is not really friendly with PNG – Remario Apr 13 '17 at 10:47
  • if your image is PNG , use the website tiny png first, it is great at file reduction and no loss in quality – Remario Apr 13 '17 at 10:51
  • This library has a lot of open issues and more importantly a memory leak – Haris Qurashi Apr 17 '17 at 07:14
  • Yes it does, you i actually sat and modified most of the issues, one step at a time, i also examined the memory leak using leak canary ad fixed it. End of the day i am software engineer, the library was very good at size reduction . If your a coder, it should not be problem to do a pull request and modify for your app. make sense? its up to you – Remario Apr 17 '17 at 12:36
  • End of the day it is open sourced, so that is expected, just fork it and do your thing, or just leave it be, its up to you. – Remario Apr 17 '17 at 14:00
1

I used this code in many projects and always it gives me good results, i remember if i choose a image having size of 5-7MB(image from 12/13 MP camera) this code returns an image of size 1MB or less than 2MB.

public static boolean validateUri(Uri uri) {
        if (uri == null)
            return false;
        else {
            String path = uri.getPath();
            return !(uri.equals(Uri.EMPTY) || path == null || path.equals("null"));
        }
    }

First we need a full image and rotate if needed.

public static Bitmap getFullSizeImage(Context context, Uri uri) {
        String filePath;
        if (validateUri(uri) && uri.toString().contains("file"))
            filePath = uri.getPath();
        else
            filePath = getRealPathFromURI(context, uri, MediaStore.Images.Media.DATA);
        if (filePath == null)
            return null;
        try {
            int rotation = 0;
            ExifInterface exifInterface = new ExifInterface(filePath);
            int exifRotation = exifInterface.getAttributeInt(
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_UNDEFINED);
            if (exifRotation != ExifInterface.ORIENTATION_UNDEFINED) {
                switch (exifRotation) {
                    case ExifInterface.ORIENTATION_ROTATE_180:
                        rotation = 180;
                        break;
                    case ExifInterface.ORIENTATION_ROTATE_270:
                        rotation = 270;
                        break;
                    case ExifInterface.ORIENTATION_ROTATE_90:
                        rotation = 90;
                        break;
                }
            }
            Matrix matrix = new Matrix();
            matrix.setRotate(rotation);
            // you can use other than 400 as required width/height
            Bitmap sourceBitmap = getBitmapFromPath(400, filePath);
            if (sourceBitmap == null)
                return null;
            return Bitmap.createBitmap(sourceBitmap, 0, 0, sourceBitmap.getWidth(),
                    sourceBitmap.getHeight(), matrix, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

Now we need a real path from URI

public static String getRealPathFromURI(Context context, Uri contentUri, String type) {
        Cursor cursor = null;
        String path = null;
        try {
            // String[] proj = { MediaStore.Images.Media.DATA };
            String[] projection = {type};
            cursor = context.getContentResolver().query(contentUri, projection, null, null, null);
            if (cursor == null)
                return null;
            int columnIndex = cursor.getColumnIndexOrThrow(type);
            cursor.moveToFirst();
            path = cursor.getString(columnIndex);
            // we choose image from drive etc.
            if (path == null)
                path = getDocumentRealPathFromUri(context, contentUri);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return path;
    }

If we choose a picture from drive etc. we still need a real path of given URI

public static String getDocumentRealPathFromUri(Context context, Uri contentUri) {
        Cursor cursor = context.getContentResolver().query(contentUri, null,
                null, null, null);
        if (cursor == null)
            return null;
        cursor.moveToFirst();
        String documentId = cursor.getString(0);
        documentId = documentId.substring(documentId.lastIndexOf(":") + 1);
        cursor.close();
        cursor = context.getContentResolver().query(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                null, MediaStore.Images.Media._ID + " = ? ",
                new String[]{documentId}, null);
        if (cursor == null)
            return null;
        cursor.moveToFirst();
        String path = cursor.getString(cursor
                .getColumnIndex(MediaStore.Images.Media.DATA));
        cursor.close();
        return path;
    }

Now we've a real path of selected image so we can get a bitmap from this path using sample size

public static Bitmap getBitmapFromPath(int size, String realPathFromURI) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(realPathFromURI, options);
        options.inSampleSize = calculateInSampleSizeUsingPower2(options, size, size);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(realPathFromURI, options);
    }

    public static int calculateInSampleSizeUsingPower2(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // raw height and width of image
        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;
    }

At this point we've a compressed bitmap, further more we can again compress this bitmap if we perform Base64 operation on a given bitmap.

public static String convertToBase64(Bitmap bitmap) {
        if (bitmap == null)
            return null;
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)) {
            String base64 = encodeToString(byteArrayOutputStream.toByteArray(), DEFAULT);
            try {
                byteArrayOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return base64;
        }
        return null;
    }

On your sever end you can decode Base64 and convert back to file stream and save your image.

Example

Bitmap bitmap = getFullSizeImage(context, selectedPhotoUri);
if(bitmap != null){
    String base64Image = convertToBase64(bitmap);
    if (base64Image != null) {
        RequestParams params = new RequestParams();
        try {
            params.put("title", "your_image_name");
            params.put("profile_picture", base64Image);
        } catch(FileNotFoundException e) {
            Log.d("error", "error catch");
        }
    }
}

Note If you don't want to perform Base64 you can use your bitmap to convert into stream and send it to your server.

Haris Qurashi
  • 2,104
  • 1
  • 13
  • 28
0

Try this function.It will reduce the size of the bitmap to 512 if its width or height is greater than 512

public static Bitmap resizeBitmap(Bitmap bm) {
        if (bm.getWidth() > maxSize || bm.getHeight() > maxSize) {
            if (bm.getWidth() > bm.getHeight()) {
                newWidth = maxSize;
                newHeight = (bm.getHeight() * maxSize) / bm.getWidth();

                bm = Bitmap.createScaledBitmap(bm, newHeight, newWidth, true);
                return bm;
            } else {
                newHeight = maxSize;
                newWidth = (bm.getWidth() * maxSize) / bm.getHeight();
                bm = Bitmap.createScaledBitmap(bm, newHeight, newWidth, true);
                return bm;
            }
        }
        return bm;
    }

You just have to pass the bitmap to this method.

The method to get the bitmap from URI is

BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 8;
        bitmap = BitmapFactory.decodeFile(fileUri.getPath(),
                options);
AbhayBohra
  • 2,047
  • 24
  • 36
0

Use this one to change image width and height

public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
    int width = bm.getWidth();
    int height = bm.getHeight();
    float scaleWidth = ((float) newWidth) / width;
    float scaleHeight = ((float) newHeight) / height;
    Matrix matrix = new Matrix();
    matrix.postScale(scaleWidth, scaleHeight);
    Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
            matrix, false);

    return resizedBitmap;
}

you can use this one for change the size ...This is the Best Example.....

  private Bitmap decodeFile(File f){
   try {
    //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(new FileInputStream(f),null,o);

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

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

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize=scale;
    return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
  } catch (FileNotFoundException e) {}
  return null;
  }
Nithinlal
  • 4,845
  • 1
  • 29
  • 40
0

If the camera image is JPEG, you can use the Bitmap compression method, like:

Bitmap bitmap = BitmapFactory.decodeStream(...uri);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try {
                int compression_factor = 50;    // represents 50% compression
                bitmap.compress(Bitmap.CompressFormat.JPEG, compression_factor, baos);

                byte[] image = baos.toByteArray();

               // now update web service asynchronously...
               ...
            } finally {
                baos.close();
            }
nkmuturi
  • 248
  • 3
  • 10
0

Convert the image into bitmap then use below method

     public static Bitmap scaleBitmap(Bitmap bitmap, int newWidth, int newHeight) {
        Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);

        float scaleX = newWidth / (float) bitmap.getWidth();
        float scaleY = newHeight / (float) bitmap.getHeight();
        float pivotX = 0;
        float pivotY = 0;

        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale(scaleX, scaleY, pivotX, pivotY);

        Canvas canvas = new Canvas(scaledBitmap);
        canvas.setMatrix(scaleMatrix);
        canvas.drawBitmap(bitmap, 0, 0, new Paint(Paint.FILTER_BITMAP_FLAG));

        return scaledBitmap;
    }
ZeroOne
  • 8,996
  • 4
  • 27
  • 45