如何在Android 13 APIs中压缩图像

huangapple go评论81阅读模式
英文:

How to compress an image in Android 13 APIs

问题

我曾经做过图像压缩。在Android X之前,它一直运行良好,但在Android版本升级后就不再工作。

public class ImageCompress {

    private static final int IMAGE_QUALITY = 99;
    private static final int MAX_SIZE_PIXELS = 1200;

    public static Bitmap decodeFile(File file, int width, int height) {
        try {
            // 解码图像尺寸
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(file), null, options);

            // 我们要缩放到的新尺寸
            final int REQUIRED_WIDTH = width;
            final int REQUIRED_HEIGHT = height;

            // 查找正确的缩放值,它应该是2的幂。
            int scale = 1;
            while (options.outWidth / scale / 2 >= REQUIRED_WIDTH
                    && options.outHeight / scale / 2 >= REQUIRED_HEIGHT)
                scale *= 2;

            // 使用inSampleSize解码
            BitmapFactory.Options newOptions = new BitmapFactory.Options();
            newOptions.inSampleSize = scale;
            return BitmapFactory.decodeStream(new FileInputStream(file), null, newOptions);

        } catch (FileNotFoundException e) {
            return null;
        }
    }

    public static File compressImage(final File file, String outputDirectory, Context context) {
        String path = file.getPath();
        String format = path.substring(path.lastIndexOf(".")).substring(1);
        Bitmap source;
        try {
            source = decodeFile(file, MAX_SIZE_PIXELS, MAX_SIZE_PIXELS);
        } catch (Exception e) {
            Log.d(ImageCompress.class.toString(), e.toString() + " 出现错误");
            return null;
        }

        Bitmap.CompressFormat compressFormat;

        // 如果是PNG或WebP格式且分辨率符合要求,则不压缩它
        if ("png".equals(format) || "webp".equals(format)) return file;

        // 选择格式
        switch (format) {
            case "png":
                compressFormat = Bitmap.CompressFormat.PNG;
                break;
            case "webp":
                compressFormat = Bitmap.CompressFormat.WEBP;
                break;
            case "gif":
                return file;
            default:
                compressFormat = Bitmap.CompressFormat.JPEG;
        }

        // 调整图像大小
        Bitmap resizedBmp;
        // 我在source.getHeight()这一行遇到错误
        if (source.getHeight() > MAX_SIZE_PIXELS || source.getWidth() > MAX_SIZE_PIXELS) {
            resizedBmp = resizeBitmap(source, MAX_SIZE_PIXELS);
        } else {
            resizedBmp = source;
        }
        File result = null;
        OutputStream fOut = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            ContentResolver resolver = BaseApplication.getInstance().getContentResolver();
            ContentValues contentValues = new ContentValues();
            contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName());
            contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/" + format);
            contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Constants.MULTIMEDIA_CHATROOM_11);
            Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
            try {
                fOut = resolver.openOutputStream(imageUri);
                result = new File(Utils.getPath(BaseApplication.getInstance(), imageUri));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        } else {
            // 如果目录不存在则创建目录
            File directory = new File(outputDirectory);
            int code = context.getPackageManager().checkPermission(
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    context.getPackageName());
            if (code == PackageManager.PERMISSION_GRANTED) {
                Log.i(ImageCompress.class.getSimpleName(), "创建目录");
                if (!directory.isDirectory() || !directory.exists()) {
                    directory.mkdirs();
                }
            } else {
                Log.i(ImageCompress.class.getSimpleName(), "访问失败");
            }

            // 压缩图像
            result = new File(outputDirectory, file.getName());

            try {
                if (!result.exists()) {
                    result.createNewFile();
                    fOut = new FileOutputStream(result);
                } else {
                    fOut = new FileOutputStream(result);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            resizedBmp.compress(compressFormat, IMAGE_QUALITY, fOut);
            fOut.flush();
            fOut.close();
            source.recycle();
            resizedBmp.recycle();

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        // 从原始图像复制EXIF方向信息
        try {
            ExifInterface oldExif = new ExifInterface(file.getPath());
            String exifOrientation = oldExif.getAttribute(ExifInterface.TAG_ORIENTATION);
            if (exifOrientation != null) {
                ExifInterface newExif = new ExifInterface(result.getPath());
                newExif.setAttribute(ExifInterface.TAG_ORIENTATION, exifOrientation);
                newExif.saveAttributes();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    private static Bitmap resizeBitmap(Bitmap source, int maxSizePixels) {
        int targetWidth, targetHeight;
        double aspectRatio;

        if (source.getWidth() > source.getHeight()) {
            targetWidth = maxSizePixels;
            aspectRatio = (double) source.getHeight() / (double) source.getWidth();
            targetHeight = (int) (targetWidth * aspectRatio);
        } else {
            targetHeight = maxSizePixels;
            aspectRatio = (double) source.getWidth() / (double) source.getHeight();
            targetWidth = (int) (targetHeight * aspectRatio);
        }

        return Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
    }
}

我的应用是为社交娱乐目的而制作的,因此由于政策原因,我不再具有像以前那样的存储访问权限。因此,我们只能使用会话范围的方式访问文件。我在source.getHeight()source.getWidth()这两行代码中遇到错误,因为它们都为零。我该如何修复这个问题,以适用于所有Android版本?

错误:

如何在Android 13 APIs中压缩图像

请注意:我注意到这个错误在从图库中选择的一些图片中出现。

英文:

I have made image compression long ago. It was working well until Android X, but above Android it’s not working.

public class ImageCompress {
private static final int IMAGE_QUALITY = 99;
private static final int MAX_SIZE_PIXELS = 1200;
public static Bitmap decodeFile(File file, int width, int height) {
try {
// Decode image size
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(file), null, options);
// The new size we want to scale to
final int REQUIRED_WIDTH = width;
final int REQUIRED_HIGHT = height;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (options.outWidth / scale / 2 >= REQUIRED_WIDTH
&& options.outHeight / scale / 2 >= REQUIRED_HIGHT)
scale *= 2;
// Decode with inSampleSize
BitmapFactory.Options newOptions = new BitmapFactory.Options();
newOptions.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(file), null, newOptions);
} catch (FileNotFoundException e) {
return null;
}
}
public static File compressImage(final File file, String outputDirectory, Context context) {
String path = file.getPath();
String format = path.substring(path.lastIndexOf(".")).substring(1);
Bitmap source;
try {
source = decodeFile(file, MAX_SIZE_PIXELS, MAX_SIZE_PIXELS);
} catch (Exception e) {
Log.d(ImageCompress.class.toString(), e.toString() + " Something happened wrong");
return null;
}
Bitmap.CompressFormat compressFormat;
// If PNG or WebP have the allowed resolution, then do not compress it
if ("png".equals(format) || "webp".equals(format)) return file;
// Select format
switch (format) {
case "png":
compressFormat = Bitmap.CompressFormat.PNG;
break;
case "webp":
compressFormat = Bitmap.CompressFormat.WEBP;
break;
case "gif":
return file;
default:
compressFormat = Bitmap.CompressFormat.JPEG;
}
// Resize image
Bitmap resizedBmp;
// I am getting an error on the source.getHeight() line
if (source.getHeight() > MAX_SIZE_PIXELS || source.getWidth() > MAX_SIZE_PIXELS) {
resizedBmp = resizeBitmap(source, MAX_SIZE_PIXELS);
} else {
resizedBmp = source;
}
File result = null;
OutputStream fOut = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ContentResolver resolver = BaseApplication.getInstance().getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName());
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/" + format);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Constants.MULTIMEDIA_CHATROOM_11);
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
try {
fOut = resolver.openOutputStream(imageUri);
result = new File(Utils.getPath(BaseApplication.getInstance(), imageUri));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else {
// create directory if not exist
File directory = new File(outputDirectory);
int code = context.getPackageManager().checkPermission(
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
context.getPackageName());
if (code == PackageManager.PERMISSION_GRANTED) {
Log.i(ImageCompress.class.getSimpleName(), "creating directory");
if (!directory.isDirectory() || !directory.exists()) {
directory.mkdirs();
}
} else {
Log.i(ImageCompress.class.getSimpleName(), "failed access");
}
// Compress image
result = new File(outputDirectory, file.getName());
try {
if (!result.exists()) {
result.createNewFile();
fOut = new FileOutputStream(result);
} else {
fOut = new FileOutputStream(result);
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
resizedBmp.compress(compressFormat, IMAGE_QUALITY, fOut);
fOut.flush();
fOut.close();
source.recycle();
resizedBmp.recycle();
} catch (Exception e) {
e.printStackTrace();
return null;
}
// Copy EXIF orientation from the original image
try {
ExifInterface oldExif = new ExifInterface(file.getPath());
String exifOrientation = oldExif.getAttribute(ExifInterface.TAG_ORIENTATION);
if (exifOrientation != null) {
ExifInterface newExif = new ExifInterface(result.getPath());
newExif.setAttribute(ExifInterface.TAG_ORIENTATION, exifOrientation);
newExif.saveAttributes();
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
private static Bitmap resizeBitmap(Bitmap source, int maxSizePixels) {
int targetWidth, targetHeight;
double aspectRatio;
if (source.getWidth() > source.getHeight()) {
targetWidth = maxSizePixels;
aspectRatio = (double) source.getHeight() / (double) source.getWidth();
targetHeight = (int) (targetWidth * aspectRatio);
} else {
targetHeight = maxSizePixels;
aspectRatio = (double) source.getWidth() / (double) source.getHeight();
targetWidth = (int) (targetHeight * aspectRatio);
}
return Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
}
}

My app is made for social entertainment purposes, so I don't have storage access like before due to policies. So we can use only the session scope way to access files. I am getting an error at line source.getHeight() and source.getWidth() as both are zero. How can I fix this this issue for all Android versions?

Error:

如何在Android 13 APIs中压缩图像

Please note: I noticed this error comes in a few pictures from the gallery.

答案1

得分: 1

如果在source.getHeight()上出现NullPointerException,那么这意味着source已被设置为null

唯一设置source的地方是在这一行:

source = decodeFile(file, MAX_SIZE_PIXELS, MAX_SIZE_PIXELS);

这意味着decodeFile方法必须返回一个空值。

查看decodeFile方法,您可以看到只有两个不同的代码路径返回一个值。第一个代码路径返回BitmapFactory.decodeStream(..)的值,据我所知,它应该永远不会返回null

因此,最有可能是FileNotFoundException捕获子句中的另一代码路径。

public static Bitmap decodeFile(File file, int width, int height) {
    try {
        ....
        return BitmapFactory.decodeStream(new FileInputStream(file), null, newOptions);

    } catch (FileNotFoundException e) {
        return null;
    }
}

这导致的结论是,在某个时候,您尝试读取一个不存在的文件,或者您没有读取权限。

由于您的应用程序较旧,如果这是一个权限问题,并且文件位于共享存储(/sdcard或其子文件夹)上,这是因为该应用程序没有使用Android 10/11引入的作用域存储系统。

英文:

If you get a NullPointerException on source.getHeight() then this means source has been set to null.

The only point where sourceis set is on the line

source = decodeFile(file, MAX_SIZE_PIXELS, MAX_SIZE_PIXELS);

This means the method decodeFile has to return a null value.

Looking at the method decodeFile you can see tha there are only two different code paths that return a value. The first code path returns the value of BitmapFactory.decodeStream(..) which AFAIK should never return null.

The it is most likely the other code path in the FileNotFoundException catch clause.

public static Bitmap decodeFile(File file, int width, int height) {
try {
....
return BitmapFactory.decodeStream(new FileInputStream(file), null, newOptions);
} catch (FileNotFoundException e) {
return null;
}
}

This leads to the conclusion that at some point you are trying to read a File that does not exist or on that you don't have read permissions.

As your app is older the chance is high that if it is a permission problem and the file is on the shared storage (/sdcard or subfolder) this is because the app does not make use of scoped storage system introduced with Android 10/11.

答案2

得分: -2

以下是代码的中文翻译:

public File 压缩图片(File 文件图像) {
  Bitmap 图标 = BitmapFactory.decodeFile(String.valueOf(文件图像));
  ByteArrayOutputStream 字节流 = new ByteArrayOutputStream();
  图标.compress(Bitmap.CompressFormat.JPEG, 70, 字节流);
  String 文件夹 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS + "/" + "Images") + "/";
  File 目录 = new File(文件夹);
  if (!目录.exists()) {
    目录.mkdirs();
  }
  File 文件 = new File(文件夹 + System.currentTimeMillis() + ".jpg");
  try {
    文件.createNewFile();
    FileOutputStream 输出流 = new FileOutputStream(文件);
    输出流.write(字节流.toByteArray());
  } catch (IOException 异常) {
    异常.printStackTrace();
  }
  return 文件;
}
英文:
public File compressImage(File fileimage) {
Bitmap icon = BitmapFactory.decodeFile(String.valueOf(fileimage));
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
icon.compress(Bitmap.CompressFormat.JPEG, 70, bytes);
String folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS + "/" + "Images") + "/";
File directory = new File(folder);
if (!directory.exists()) {
directory.mkdirs();
}
File f = new File(folder + System.currentTimeMillis() + ".jpg");
try {
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
return f;
}

huangapple
  • 本文由 发表于 2023年7月3日 21:07:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76605043.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定