Cách giảm kích thước tệp Hình ảnh trước khi tải lên máy chủ


82

Rất nhiều ứng dụng cho phép chia sẻ hình ảnh, được chọn từ thư viện.

Họ có tải lên tệp hình ảnh gốc không? Cái nào giống như 1-3 mb? Hay họ xử lý?

Trong mọi trường hợp, làm cách nào tôi có thể lấy hình ảnh từ một đường dẫn tệp, giảm kích thước của nó bằng cách giảm độ phân giải và lưu nó ở một số nơi khác và thử tải lên?

Tôi đã thử:

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

Nhưng đây có phải là cách họ làm đúng? Vì tôi đã thấy câu trả lời gợi ý compression operation takes rather big amount of time ở đây


1
hình ảnh chuyển đổi trong base64
shivang Trivedi

Tôi gặp ác mộng bằng base64 :( tôi gặp phải vấn đề bộ nhớ chuyển đổi rất lớn (2MB) các tập tin để base64.
user1537779

3
Base64 sẽ tăng kích thước tệp. Giải pháp của bạn nói chung là đúng.
trebron

Mã của bạn đã giúp ích rất nhiều! Dành một vài ngày để cố gắng làm đúng những gì bạn đang làm. Đã đi qua điều này và nó hoạt động tốt! Cảm ơn rất nhiều!
user3079872

Câu trả lời:


64

Tôi sử dụng chức năng này để giảm kích thước hình ảnh trước khi tải lên, nó làm giảm kích thước hình ảnh xuống gần 200 KB và giữ chất lượng tương đối tốt, bạn có thể sửa đổi nó để đáp ứng mục đích của mình bằng cách thay đổi REQUIRED_SIZE và 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;
    }
}

LƯU Ý: Tôi thay đổi kích thước và ghi đè hình ảnh tệp gốc trong chức năng này, bạn cũng có thể ghi nó vào tệp khác.

Tôi hy vọng nó có thể giúp bạn.


2
REQUIRED_SIZE là gì? nó là tỷ lệ phần trăm của tỷ lệ hoặc dimen cố định của hình ảnh đầu ra?
Usman Rana,

8
Phương pháp của bạn rất tuyệt nhưng tôi không biết tại sao nó lại xoay hình ảnh !!! Tôi nhận được một hình ảnh được xoay sau khi giảm kích thước của nó bằng chức năng này !!
SlimenTN

1
Hoạt động tốt, sạch sẽ và thoải mái
Acheme Paul

1
@Nainal nó được một lúc và tôi quên mã tôi đã viết :( bạn có thể kiểm bằng cách kiểm tra anyway :)
mbH

4
Goon trả lời nhưng .. tôi là người duy nhất nghĩ rằng tên hàm không phù hợp?
mê cung

51

Điều này đang hoạt động tốt Hãy thử điều này

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

}

Câu trả lời thực sự hữu ích.
Arun Badole

4
cái này không giúp được gì, làm thế nào để sử dụng cái này?
Syed Raza Mehdi

cảm ơn rất nhiều. Đây là câu trả lời duy nhất phù hợp với tôi.
Däñish Shärmà

3
Bạn đang nén hình ảnh dưới dạng JPEG nhưng đặt tên nó bằng phần mở rộng ".png". Không phải là nó thực sự quan trọng anyway ... nhưng vẫn
Logain

1
@Biraj Zalavadia Tôi đang cố gắng sử dụng mã này, nhưng nhận được null trên unscaledBitmap bất kỳ ý tưởng nào tại sao nó có thể trả về null.
reetu

7

mã này giảm kích thước hình ảnh

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

giải pháp này có hiệu suất thấp vì gọi phương thức nén nhiều lần.
mehdi

Câu trả lời này rất hữu ích trong việc hiểu mã được phân loại. để chia tỷ lệ hình ảnh, điều này đã giúp tôi tổng hợp câu trả lời mà tôi đã đăng ở đây. Trong trường hợp nó giúp - phần mà không làm việc cho tôi là trận chung kết Bitmap bitmapvẫn là kích thước giống như đầu vào gốcBitmap image
Gene Bo

Nó đã giúp tôi hoàn thành mã của mình vì nó có những thông tin chi tiết tuyệt vời như kích thước tối đa và những thứ khác. Nó thực sự rất hữu ích mặc dù nó không phải là một mã hoàn chỉnh.
Thiago Silva

5

Đây là một giải pháp được xử lý trong bộ nhớ và không yêu cầu thực sự tạo tệp trên hệ thống tệp.

Từ một số Fragment, sau khi người dùng chọn một tệp hình ảnh:

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

Nhờ vào:


1
Giải pháp tốt! Tôi khuyên bạn chỉ nên thêm kiểm tra kích thước và đặt scaleDivider dựa trên giá trị này vì rất nhiều hình ảnh không cố định trên 2MB!
Androidz

2

Đây là giải pháp của tôi

/*
* This procedure will replace the original image
* So you need to do a tmp copy to send before reduce
*/
public static boolean reduceImage(String path, long maxSize) {
    File img = new File(path);
    boolean result = false;
    BitmapFactory.Options options = new BitmapFactory.Options();
    Bitmap bitmap = null;
    options.inSampleSize=1;
    while (img.length()>maxSize) {
        options.inSampleSize = options.inSampleSize+1;
        bitmap = BitmapFactory.decodeFile(path, options);
        img.delete();
        try
            {
                FileOutputStream fos = new FileOutputStream(path);
                img.compress(path.toLowerCase().endsWith("png")?
                                Bitmap.CompressFormat.PNG:
                                Bitmap.CompressFormat.JPEG, 100, fos);
                fos.close();
                result = true;
             }catch (Exception errVar) { 
                errVar.printStackTrace(); 
             }
    };
    return result;
}

CHỈNH SỬA Đã xóa các lệnh gọi thủ tục khác


Có rất nhiều thao tác với tệp ở đây.
Tim Cooper

Tôi không hiểu những gì bạn có nghĩa là
San Juan

Có một vòng lặp và bạn có khả năng ghi hình ảnh vào hệ thống tệp nhiều lần, một lần cho mỗi lần lặp lại trong vòng lặp. Nó có thể được thực hiện trong bộ nhớ thay thế? Tôi nghĩ rằng tập tin sẽ được khoảng. 1/4 kích thước mỗi lần lặp nên nó không phải là vấn đề lớn. Tôi nghĩ rằng phương pháp của bạn sẽ hoạt động và tôi đã làm điều gì đó tương tự nhưng không có vòng lặp.
Tim Cooper

1
@TimCooper Tôi có thể tìm giải pháp của bạn ở đâu?
San Juan

0

đây là phương pháp tôi đang sử dụng trong kotlin :

Lưu ý: tôi đã thử nó với 3 hình ảnh, mỗi hình ảnh là 6 mb và trong một cuộc gọi

 private fun Bitmap.compress(cacheDir: File, f_name: String): File? {
    val f = File(cacheDir, "user$f_name.jpg")
    f.createNewFile()
    ByteArrayOutputStream().use { stream ->
        compress(Bitmap.CompressFormat.JPEG, 70, stream)
        val bArray = stream.toByteArray()
        FileOutputStream(f).use { os -> os.write(bArray) }
    }//stream
    return f
}

-1

Sử dụng phương pháp này để trả về một hình ảnh bitmap được nén vào khoảng 200 KB. Bạn có thể cấu hình nó để có được hình ảnh bitmap ở bất kỳ kích thước nào bạn muốn.

public static Bitmap scaleImage(Context context, Uri photoUri) throws IOException {
    InputStream is = context.getContentResolver().openInputStream(photoUri);
    BitmapFactory.Options dbo = new BitmapFactory.Options();
    dbo.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(is, null, dbo);
    is.close();

    int rotatedWidth, rotatedHeight;
    int orientation = getOrientation(context, photoUri);

    if (orientation == 90 || orientation == 270) {
        rotatedWidth = dbo.outHeight;
        rotatedHeight = dbo.outWidth;
    } else {
        rotatedWidth = dbo.outWidth;
        rotatedHeight = dbo.outHeight;
    }

    Bitmap srcBitmap;
    is = context.getContentResolver().openInputStream(photoUri);
    if (rotatedWidth > MAX_IMAGE_DIMENSION || rotatedHeight > MAX_IMAGE_DIMENSION) {
        float widthRatio = ((float) rotatedWidth) / ((float) MAX_IMAGE_DIMENSION);
        float heightRatio = ((float) rotatedHeight) / ((float) MAX_IMAGE_DIMENSION);
        float maxRatio = Math.max(widthRatio, heightRatio);

        // Create the bitmap from file
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = (int) maxRatio;
        srcBitmap = BitmapFactory.decodeStream(is, null, options);
    } else {
        srcBitmap = BitmapFactory.decodeStream(is);
    }
    is.close();

    /*
     * if the orientation is not 0 (or -1, which means we don't know), we
     * have to do a rotation.
     */
    if (orientation > 0) {
        Matrix matrix = new Matrix();
        matrix.postRotate(orientation);

        srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(),
                srcBitmap.getHeight(), matrix, true);
    }

    String type = context.getContentResolver().getType(photoUri);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    if (type.equals("image/png")) {
        srcBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
    } else if (type.equals("image/jpg") || type.equals("image/jpeg")) {
        srcBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    }
    byte[] bMapArray = baos.toByteArray();
    baos.close();
    return BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length);
}

1
Việc triển khai phương thức getOrientation ở đâu?
Gaurav Sarma

1
getOrientation là mất tích
codeKiller

1
Vui lòng thêm tất cả các phương thức bắt buộc / phụ thuộc vào câu trả lời.
Mohammedsalim Shivani
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.