How to reduce an Image file size before uploading to a server
Solution 1
I use this function to reduce the size of image before uploading it, it reduces the image size to nearly 200 KB and keep the quality relatively good, u may modify it to fulfill your purpose by changing the REQUIRED_SIZE and inSampleSize:
public File saveBitmapToFile(File file){
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, 100 , outputStream);
return file;
} catch (Exception e) {
return null;
}
}
NOTE: I resize and override the original file image in this function, u may write it to another file as well.
I hope it may help you.
Solution 2
This is working great Try this
private String decodeFile(String path,int DESIREDWIDTH, int DESIREDHEIGHT) {
String strMyImagePath = null;
Bitmap scaledBitmap = null;
try {
// Part 1: Decode image
Bitmap unscaledBitmap = ScalingUtilities.decodeFile(path, DESIREDWIDTH, DESIREDHEIGHT, ScalingLogic.FIT);
if (!(unscaledBitmap.getWidth() <= DESIREDWIDTH && unscaledBitmap.getHeight() <= DESIREDHEIGHT)) {
// Part 2: Scale image
scaledBitmap = ScalingUtilities.createScaledBitmap(unscaledBitmap, DESIREDWIDTH, DESIREDHEIGHT, ScalingLogic.FIT);
} else {
unscaledBitmap.recycle();
return path;
}
// Store to tmp file
String extr = Environment.getExternalStorageDirectory().toString();
File mFolder = new File(extr + "/TMMFOLDER");
if (!mFolder.exists()) {
mFolder.mkdir();
}
String s = "tmp.png";
File f = new File(mFolder.getAbsolutePath(), s);
strMyImagePath = f.getAbsolutePath();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 75, fos);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
scaledBitmap.recycle();
} catch (Throwable e) {
}
if (strMyImagePath == null) {
return path;
}
return strMyImagePath;
}
ScalingUtilities.java
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
/**
* Class containing static utility methods for bitmap decoding and scaling
*
* @author
*/
public class ScalingUtilities {
/**
* Utility function for decoding an image resource. The decoded bitmap will
* be optimized for further scaling to the requested destination dimensions
* and scaling logic.
*
* @param res The resources object containing the image data
* @param resId The resource id of the image data
* @param dstWidth Width of destination area
* @param dstHeight Height of destination area
* @param scalingLogic Logic to use to avoid image stretching
* @return Decoded bitmap
*/
public static Bitmap decodeResource(Resources res, int resId, int dstWidth, int dstHeight,
ScalingLogic scalingLogic) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth,
dstHeight, scalingLogic);
Bitmap unscaledBitmap = BitmapFactory.decodeResource(res, resId, options);
return unscaledBitmap;
}
public static Bitmap decodeFile(String path, int dstWidth, int dstHeight,
ScalingLogic scalingLogic) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth,
dstHeight, scalingLogic);
Bitmap unscaledBitmap = BitmapFactory.decodeFile(path, options);
return unscaledBitmap;
}
/**
* Utility function for creating a scaled version of an existing bitmap
*
* @param unscaledBitmap Bitmap to scale
* @param dstWidth Wanted width of destination bitmap
* @param dstHeight Wanted height of destination bitmap
* @param scalingLogic Logic to use to avoid image stretching
* @return New scaled bitmap object
*/
public static Bitmap createScaledBitmap(Bitmap unscaledBitmap, int dstWidth, int dstHeight,
ScalingLogic scalingLogic) {
Rect srcRect = calculateSrcRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(),
dstWidth, dstHeight, scalingLogic);
Rect dstRect = calculateDstRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(),
dstWidth, dstHeight, scalingLogic);
Bitmap scaledBitmap = Bitmap.createBitmap(dstRect.width(), dstRect.height(),
Config.ARGB_8888);
Canvas canvas = new Canvas(scaledBitmap);
canvas.drawBitmap(unscaledBitmap, srcRect, dstRect, new Paint(Paint.FILTER_BITMAP_FLAG));
return scaledBitmap;
}
/**
* ScalingLogic defines how scaling should be carried out if source and
* destination image has different aspect ratio.
*
* CROP: Scales the image the minimum amount while making sure that at least
* one of the two dimensions fit inside the requested destination area.
* Parts of the source image will be cropped to realize this.
*
* FIT: Scales the image the minimum amount while making sure both
* dimensions fit inside the requested destination area. The resulting
* destination dimensions might be adjusted to a smaller size than
* requested.
*/
public static enum ScalingLogic {
CROP, FIT
}
/**
* Calculate optimal down-sampling factor given the dimensions of a source
* image, the dimensions of a destination area and a scaling logic.
*
* @param srcWidth Width of source image
* @param srcHeight Height of source image
* @param dstWidth Width of destination area
* @param dstHeight Height of destination area
* @param scalingLogic Logic to use to avoid image stretching
* @return Optimal down scaling sample size for decoding
*/
public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight,
ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.FIT) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return srcWidth / dstWidth;
} else {
return srcHeight / dstHeight;
}
} else {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return srcHeight / dstHeight;
} else {
return srcWidth / dstWidth;
}
}
}
/**
* Calculates source rectangle for scaling bitmap
*
* @param srcWidth Width of source image
* @param srcHeight Height of source image
* @param dstWidth Width of destination area
* @param dstHeight Height of destination area
* @param scalingLogic Logic to use to avoid image stretching
* @return Optimal source rectangle
*/
public static Rect calculateSrcRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight,
ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.CROP) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
final int srcRectWidth = (int)(srcHeight * dstAspect);
final int srcRectLeft = (srcWidth - srcRectWidth) / 2;
return new Rect(srcRectLeft, 0, srcRectLeft + srcRectWidth, srcHeight);
} else {
final int srcRectHeight = (int)(srcWidth / dstAspect);
final int scrRectTop = (int)(srcHeight - srcRectHeight) / 2;
return new Rect(0, scrRectTop, srcWidth, scrRectTop + srcRectHeight);
}
} else {
return new Rect(0, 0, srcWidth, srcHeight);
}
}
/**
* Calculates destination rectangle for scaling bitmap
*
* @param srcWidth Width of source image
* @param srcHeight Height of source image
* @param dstWidth Width of destination area
* @param dstHeight Height of destination area
* @param scalingLogic Logic to use to avoid image stretching
* @return Optimal destination rectangle
*/
public static Rect calculateDstRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight,
ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.FIT) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return new Rect(0, 0, dstWidth, (int)(dstWidth / srcAspect));
} else {
return new Rect(0, 0, (int)(dstHeight * srcAspect), dstHeight);
}
} else {
return new Rect(0, 0, dstWidth, dstHeight);
}
}
}
Solution 3
Here's a solution that's handled in memory and doesn't require actually generating a file on the file system.
From some Fragment, after user selects an image file:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
if (imageReturnedIntent == null
|| imageReturnedIntent.getData() == null) {
return;
}
// aiming for ~500kb max. assumes typical device image size is around 2megs
int scaleDivider = 4;
try {
// 1. Convert uri to bitmap
Uri imageUri = imageReturnedIntent.getData();
Bitmap fullBitmap = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), imageUri);
// 2. Get the downsized image content as a byte[]
int scaleWidth = fullBitmap.getWidth() / scaleDivider;
int scaleHeight = fullBitmap.getHeight() / scaleDivider;
byte[] downsizedImageBytes =
getDownsizedImageBytes(fullBitmap, scaleWidth, scaleHeight);
// 3. Upload the byte[]; Eg, if you are using Firebase
StorageReference storageReference =
FirebaseStorage.getInstance().getReference("/somepath");
storageReference.putBytes(downsizedImageBytes);
}
catch (IOException ioEx) {
ioEx.printStackTrace();
}
}
public byte[] getDownsizedImageBytes(Bitmap fullBitmap, int scaleWidth, int scaleHeight) throws IOException {
Bitmap scaledBitmap = Bitmap.createScaledBitmap(fullBitmap, scaleWidth, scaleHeight, true);
// 2. Instantiate the downsized image content as a byte[]
ByteArrayOutputStream baos = new ByteArrayOutputStream();
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] downsizedImageBytes = baos.toByteArray();
return downsizedImageBytes;
}
Thanks to:
- Convert uri to bitmap: https://stackoverflow.com/a/4717740/2162226
- Scaling/resize step (from this thread): https://stackoverflow.com/a/50192169/2162226
- Save byte[] to Firebase : https://stackoverflow.com/a/40886397/2162226
Solution 4
this code reduce size image
private Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//Compression quality, here 100 means no compression, the storage of compressed data to baos
int options = 90;
while (baos.toByteArray().length / 1024 > 400) { //Loop if compressed picture is greater than 400kb, than to compression
baos.reset();//Reset baos is empty baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);//The compression options%, storing the compressed data to the baos
options -= 10;//Every time reduced by 10
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//The storage of compressed data in the baos to ByteArrayInputStream
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//The ByteArrayInputStream data generation
return bitmap;
}
Solution 5
You can use this library named Compressor.
Compress Image File
val compressedImageFile = Compressor.compress(context, actualImageFile)
Compress Image File to specific destination
val compressedImageFile = Compressor.compress(context, actualImageFile) {
default()
destination(myFile)
}
Custom Compressor
Using default constraint and custom partial of it
val compressedImageFile =
Compressor.compress(context, actualImageFile) {
default(width = 640, format = Bitmap.CompressFormat.WEBP)
}
Full custom constraint
val compressedImageFile = Compressor.compress(context, actualImageFile) {
resolution(1280, 720)
quality(80)
format(Bitmap.CompressFormat.WEBP)
size(2_097_152) // 2 MB
}
Using own custom constraint
class MyLowerCaseNameConstraint: Constraint {
override fun isSatisfied(imageFile: File): Boolean {
return imageFile.name.all { it.isLowerCase() }
}
override fun satisfy(imageFile: File): File {
val destination = File(imageFile.parent, imageFile.name.toLowerCase())
imageFile.renameTo(destination)
return destination
}
}
val compressedImageFile = Compressor.compress(context, actualImageFile) {
constraint(MyLowerCaseNameConstraint()) // your own constraint
quality(80) // combine with compressor constraint
format(Bitmap.CompressFormat.WEBP)
}
Create Kotlin extension -
fun Compression.lowerCaseName() {
constraint(MyLowerCaseNameConstraint())
}
val compressedImageFile = Compressor.compress(context, actualImageFile) {
lowerCaseName() // your own extension
quality(80) // combine with compressor constraint
format(Bitmap.CompressFormat.WEBP)
}
Compress image with Kotlin coroutines -
// e.g calling from activity lifecycle scope
lifecycleScope.launch {
val compressedImageFile = Compressor.compress(context, actualImageFile)
}
// calling from global scope
GlobalScope.launch {
val compressedImageFile = Compressor.compress(context, actualImageFile)
}
Run Compressor in main thread -
val compressedImageFile = Compressor.compress(context, actualImageFile, Dispatchers.Main)
user1537779
Updated on March 31, 2021Comments
-
user1537779 about 3 years
Lot of application allow sharing an image, which is picked from the gallery.
Do they upload the original image file? Which is like 1-3 mb? Or do they process?
In any case, how can I take the image from a filepath, reduce its size by lowering the resolution and save it some where else and the try to upload?
I tried:
Bitmap photo = decodeSampledBitmapFromFile(filePath, DESIRED_WIDTH, DESIRED_HEIGHT); FileOutputStream out = new FileOutputStream(filePath); photo.compress(Bitmap.CompressFormat.JPEG, 100, out); public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); final int height = options.outHeight; final int width = options.outWidth; options.inPreferredConfig = Bitmap.Config.ARGB_8888; int inSampleSize = 1; if (height > reqHeight) { inSampleSize = Math.round((float) height / (float) reqHeight); } int expectedWidth = width / inSampleSize; if (expectedWidth > reqWidth) { inSampleSize = Math.round((float) width / (float) reqWidth); } options.inSampleSize = inSampleSize; options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(path, options); }
But is this they right way to do? Because I have seen answers suggesting
compression operation takes rather big amount of time
here -
Arun Badole over 9 yearsReally useful Answer.
-
Syed Raza Mehdi about 9 yearsthis didn't help, how to use this?
-
Däñish Shärmà almost 8 yearsthanks alot. This is the only answer which worked for me.
-
Logain almost 8 yearsYou are compressing the image as JPEG but naming it with ".png" extension. Not that it actually matters anyway... but still
-
Kishan Vaghela almost 8 yearsfollow this if you got OutOfMemory
-
Gaurav Sarma over 7 yearsWhere is the getOrientation method implementation ?
-
Akshay Kumar about 7 years@Biraj Zalavadia Where and how to call this ------------------
-
Akshay Kumar about 7 years@MBH How to all this method and where i will get the file argument from
-
MBH about 7 years@AkshayKumar the parameter file, will be the file that you will write to.
new File("your_image_to_be_comopressed_full_path");
. The output file is thesame file
if successfully done.Null
if something went wrong. NOTICE that to not override the existing image, remove the linefile.createNewFile();
and write the image to another file instead. -
codeKiller almost 7 yearsgetOrientation is missing
-
Usman Rana almost 7 yearswhat is REQUIRED_SIZE ? is it percentage of scaling or fixed dimen of output image?
-
reetu over 6 years@Biraj Zalavadia I am trying to use this code, but getting null on unscaledBitmap any idea why it might return null.
-
SlimenTN about 6 yearsYour method is great but I don't know why it's rotating the image !!! I get a rotated image after reducing it's size using this function !!
-
Mohammedsalim Shivani about 6 yearsPlease add all the required/depended methods in the answer.
-
Tim Cooper about 6 yearsThere are a lot of file operations here.
-
San Juan about 6 yearsI don't understand what you mean
-
Tim Cooper about 6 yearsThere is a loop and you potentially write the image to the file-system multiple times, once for each iteration in the loop. Could it be done in memory instead? I think the file will be approx. 1/4 the size each iteration so it's not a big deal. I think your method will work and I did something similar but without a loop.
-
San Juan about 6 years@TimCooper Where can I find your solution?
-
mehdi almost 6 yearsthis solution has low performance because of calling compress method several times.
-
Mabz over 5 years@reetu I got the message too and I suspect it is because I am using the Uri.getPath() operation to get the filename.
-
Acheme Paul over 5 yearsWorks well, clean and nice
-
Nainal over 5 years@MBH what should be the REQUIRED_SIZE for image size upto 4MB?
-
MBH over 5 years@Nainal it’s been a while and i forgot the code i wrote :( you may checkout by testing anyway :)
-
Awadesh over 5 yearshow can we define int DESIREDWIDTH, int DESIREDHEIGHT, I have to maintain the image originality and show on different devices, please help
-
HendraWD about 5 yearsI think you should think of cleanup mechanism of your temporary image and folder. It will be better if the folder using cache/data folder of the application.
-
varun about 5 years@MBH I can't say if those are sad and happy ASCII emoticons or just parentheses preceded by a colon... hmmmmmmm... xD
-
varun about 5 yearsAh yes, it works elegantly. For some reason, using
image.compress(Bitmap.CompressFormat.FORMAT, QUALITY, fileOutputStream)
did not work. However, your solution which uses theBitmapFactory
produces 8x size reduction without compromising quality! Thanks! -
Gene Bo almost 5 yearsThis answer was helpful in understanding the code nec. for image scaling, which helped me put together the answer I posted here as well. In case it helps - the part that didn't work for me is that the final
Bitmap bitmap
was still the same size as the original inputBitmap image
-
mazend about 4 yearsGoon answer but.. am I the only one who think the function name is not appropriate?
-
MBH about 4 years@mazend You are right, you are free to edit my answer and put a better name :)
-
Thiago Silva about 4 yearsIt helped me to complete my code as it comes with great insights like the max size and other things. Its really very helpfull although its not a complete code.
-
Ast about 4 yearsMBH as @Slimen says if I take a picture in protrait mode, it is rotated...but I can't understand why
-
Androidz almost 4 yearsNice solution! I recommend just adding size check and setting scaleDivider based on this value as images very a lot are not constant on 2MB!
-
adegolyer over 3 yearsFor those with rotation problems: You can get image orientation from the originals exif data and write it to the copy.
-
PERAM BRAHMA REDDY about 3 yearsprivate ImageCodecInfo GetEncoder(ImageFormat format) { /*/ Hope this will work.Thanks in advance /*/ ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders(); foreach (ImageCodecInfo codec in codecs) { if (codec.FormatID == format.Guid) { return codec; } } return null; }
-
Fattie about 3 yearsThanks so much GeneBo! This is like a top-100 answer on the entire site, buried here in obscurity. Sending a token bounty, thanks. I was just wondering, for completeness could U add a line preserving exif data (for us iOS programmers passing by :) ) Gene ROCKS !! what an awesome answer.
-
Gene Bo about 3 yearsThanks for the kind word :) .
exif data
is outside my range. This search seemed to turn up some answers on SO: image preserving exif data android .. opting for Android to follow w/this answer. Perhaps you see a way to tie one of those in here? -
Fattie about 3 yearsGot it. @GeneBo I sent a small bounty to say thanks - cheers
-
Gene Bo about 3 yearsHi @Fattie, always good to hear an answer on SO helps someone, but wow what an unexpected surprise I found when checking back in after the weekend. That's very generous of you. You replenished the bounties I've given out in the last year - pretty cool, I appreciate it. And with your strong rank here, I take the gesture as a compliment .. no joke, Thank you
-
Emon Hossain Munna over 2 yearsHey Emon Thanks Mate! Its Your Friend Of Friends (Habib UITS)
-
Gk Mohammad Emon over 2 yearsHi @EmonHossainMunna , Nice to meet you. See you soon.
-
KJEjava48 about 2 years@GeneBo Can we choose any compression format instead of Bitmap.CompressFormat.JPEG ?? Does this should be the same of the actual file ??
-
Bita Mirshafiee about 2 yearsfor image rotation problem, do this: val oldExif = ExifInterface(file.path) val exifOrientation = oldExif.getAttribute(ExifInterface.TAG_ORIENTATION) if (exifOrientation != null) { val newExif = ExifInterface(file.path) newExif.setAttribute(ExifInterface.TAG_ORIENTATION, exifOrientation) newExif.saveAttributes() }