在游戏中新增了一个客服功能,要求玩家能够在app内发送图片。测试时发现图片太大,发送出去,于是选择在前端压缩代码。
测试时发现图片因为太大了,直接被服务器拒绝。报错413 Request Entity Too Large
,如下图:
实现思路
使用BitmapFactory
和Bitmap
类进行图片压缩,并保存压缩后的图片路径,然后在发送的时候发送压缩后的图片。
方式一:压缩图片质量,并修改图片尺寸
1、压缩图片路径
- 压缩后的图片保存在应用的缓存目录下,文件名添加了
compressed_
前缀
2、图片压缩逻辑
- 使用
BitmapFactory.Options
和isSampleSize
对图片进行采样缩小
- 使用
Bitmap.compress()
以指定质量保存压缩图片
3、返回压缩路径
- 将压缩后的图片路径通过 JSON 返回,替代原始图片路径。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| private void handleGalleryResult(Intent data) { Log.d("ImageUri", data.toString()); if (data.getData() != null) { Uri selectedImageUri = data.getData(); String originalImagePath = getPathFromUri(selectedImageUri); Log.d("ImageUri", "Original Path: " + originalImagePath);
try { String compressedImagePath = compressImage(originalImagePath); Log.d("ImageUri", "Compressed Path: " + compressedImagePath);
JSONObject jsonobj = new JSONObject(); jsonobj.put("ImageUrl", compressedImagePath); jsonobj.put("type", "selectedImageUrl"); String str = String.format("NativeAndroid.javaCallback('%s')", jsonobj.toString()); callJsGlobalFunc(str); } catch (Exception e) { e.printStackTrace(); } } }
private String compressImage(String imagePath) throws Exception { File originalFile = new File(imagePath); File compressedFile = new File(getActivity().getCacheDir(), "compressed_" + originalFile.getName());
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath, options);
options.inSampleSize = calculateInSampleSize(options, 800, 800); options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
try (FileOutputStream fos = new FileOutputStream(compressedFile)) { bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos); }
bitmap.recycle();
return compressedFile.getAbsolutePath(); }
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { 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;
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } }
return inSampleSize; }
|
方式二:只压缩图片质量,不调整图片尺寸
关键改动
1、不调整图片分辨率:
- 不使用
BitmapFactory.Options
或 inSampleSize
来缩小图片。
- 直接加载原始图片,并保持其原始尺寸。
2、仅降低图片质量:
- 使用
Bitmap.compress(Bitmap.CompressFormat.JPEG, quality, fos)
减少文件大小。
- 参数
quality
设置为 80(可以根据需求调整,范围为 0-100)。
3、保存路径:
- 保存到应用缓存目录,文件名前缀为 compressed_,以便与原始图片区分。
注意事项
如果图片格式是非 JPEG(如 PNG),可以根据需求替换为 Bitmap.CompressFormat.PNG
,但 PNG 不支持质量压缩。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| private void handleGalleryResult(Intent data) { Log.d("ImageUri", data.toString()); if (data.getData() != null) { Uri selectedImageUri = data.getData(); String originalImagePath = getPathFromUri(selectedImageUri); Log.d("ImageUri", "Original Path: " + originalImagePath);
try { String compressedImagePath = compressImageWithoutChangingSize(originalImagePath); Log.d("ImageUri", "Compressed Path: " + compressedImagePath);
JSONObject jsonobj = new JSONObject(); jsonobj.put("ImageUrl", compressedImagePath); jsonobj.put("type", "selectedImageUrl"); String str = String.format("NativeAndroid.javaCallback('%s')", jsonobj.toString()); callJsGlobalFunc(str); } catch (Exception e) { e.printStackTrace(); } } }
private String compressImageWithoutChangingSize(String imagePath) throws Exception { File originalFile = new File(imagePath); File compressedFile = new File(getActivity().getCacheDir(), "compressed_" + originalFile.getName());
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
try (FileOutputStream fos = new FileOutputStream(compressedFile)) { bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos); }
bitmap.recycle();
return compressedFile.getAbsolutePath(); }
|
方式三:将图片压缩到指定尺寸和指定大小
经过前面两种方法测试后,发现部分图片还是会存在问题,因为现在手机的分辨率比较高,很多图片都比较大,如果按照比例压缩,还是可能出现图片太大的情况。我这里是将所有图片压缩到不超过300KB,这个可以根据自己想要的去调。
思路
根据前面两种方式的体验,将压缩的方式修改为循环压缩,每次压缩的比例都会小一点点,直到图片最后的大小小于300KB为止。如果图片原图就小于300KB。则直接不压缩。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| private void handleGalleryResult(Intent data) { Log.d("ImageUri", data.toString()); if (data.getData() != null) { Uri selectedImageUri = data.getData(); String originalImagePath = getPathFromUri(selectedImageUri); Log.d("ImageUri", "Original Path: " + originalImagePath);
try { String finalImagePath = compressAndResizeImage(originalImagePath, 300 * 1024); Log.d("ImageUri", "Final Path: " + finalImagePath);
JSONObject jsonobj = new JSONObject(); jsonobj.put("ImageUrl", finalImagePath); jsonobj.put("type", "selectedImageUrl"); String str = String.format("NativeAndroid.javaCallback('%s')", jsonobj.toString()); callJsGlobalFunc(str); } catch (Exception e) { e.printStackTrace(); } } }
private String compressAndResizeImage(String imagePath, long targetSizeInBytes) throws Exception { File originalFile = new File(imagePath); long originalSize = originalFile.length();
if (originalSize <= targetSizeInBytes) { Log.d("ImageCompression", "Image size is already within the target: " + originalSize + " bytes"); return imagePath; }
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = false; Bitmap originalBitmap = BitmapFactory.decodeFile(imagePath, options);
int newWidth = options.outWidth / 2; int newHeight = options.outHeight / 2; Bitmap resizedBitmap = Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, true);
File compressedFile = new File(getActivity().getCacheDir(), "compressed_" + originalFile.getName());
int quality = 100; while (true) { try (FileOutputStream fos = new FileOutputStream(compressedFile)) { resizedBitmap.compress(Bitmap.CompressFormat.JPEG, quality, fos); }
long compressedSize = compressedFile.length(); Log.d("ImageCompression", "Compressed size: " + compressedSize + " bytes at quality: " + quality);
if (compressedSize <= targetSizeInBytes || quality <= 10) { break; }
quality -= 5; }
originalBitmap.recycle(); resizedBitmap.recycle();
return compressedFile.getAbsolutePath(); }
|