英文:
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版本?
错误:
请注意:我注意到这个错误在从图库中选择的一些图片中出现。
英文:
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:
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 source
is 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;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论