Android Studio如何实现文件高压缩后上传至AWS Amplify S3云存储
AWS Amplify S3 上传前文件最优压缩实现方案
核心原则:所有压缩逻辑在调用Amplify上传接口前执行,不要将原始文件流直接传入上传方法,将压缩完成后的流作为上传输入源即可,分图片、视频两类场景分别处理:
图片压缩(可稳定实现1.5MB文件压缩至120KB左右的效果)
不需要引入重型第三方库,用Android原生API就能实现目标压缩率,逻辑分两步:
- 第一步采样压缩:解码图片前先读取原图尺寸,按比例计算采样值,只加载适配移动端观看的尺寸到内存,常规场景把图片长边限制在1280px即可,既避免加载全尺寸图导致OOM,也从根源减少文件体积
- 第二步阶梯质量压缩:优先选WebP有损格式(压缩率比JPEG高30%左右,低版本安卓兼容JPEG),从80%质量开始逐步下调,每次压缩后判断输出字节数组大小,直到落到100KB-130KB的目标区间,最低质量不低于30%避免画质劣化过于明显
用这套逻辑处理普通手机拍摄的1.5MB左右照片,最终输出体积基本稳定在120KB上下,肉眼几乎看不出画质差异。
视频压缩
视频不要手写编码逻辑,直接用系统自带MediaCodec做转码即可,参数按以下设置就能平衡体积和画质:
- 分辨率:原视频分辨率高于1080p的压到1080p,普通短视频压到720p足够移动端观看
- 码率设置为1.5Mbps-2Mbps,帧率固定25fps,编码用H.264格式保证全设备兼容
- 压缩属于耗时计算操作,必须放在工作线程/协程中执行,压缩结果输出到应用私有目录的临时缓存文件,上传完成后及时删除临时文件避免占用存储。
改造后的可直接复用的上传代码
首先实现图片压缩工具方法:
private InputStream compressImage(Context context, Uri imageUri, long targetSizeKB) throws IOException { // 第一次解码仅读取原图尺寸,不加载像素到内存 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; try (InputStream boundsIn = context.getContentResolver().openInputStream(imageUri)) { BitmapFactory.decodeStream(boundsIn, null, options); } // 计算采样率,限制长边最大为1280px int maxEdge = 1280; int sampleSize = 1; if (options.outHeight > maxEdge || options.outWidth > maxEdge) { int heightRatio = Math.round((float) options.outHeight / (float) maxEdge); int widthRatio = Math.round((float) options.outWidth / (float) maxEdge); sampleSize = Math.min(heightRatio, widthRatio); } options.inSampleSize = sampleSize; options.inJustDecodeBounds = false; // 采样解码得到缩小后的位图 Bitmap sampledBitmap; try (InputStream bitmapIn = context.getContentResolver().openInputStream(imageUri)) { sampledBitmap = BitmapFactory.decodeStream(bitmapIn, null, options); } // 阶梯调整压缩质量,直到达到目标体积 ByteArrayOutputStream compressOut = new ByteArrayOutputStream(); int quality = 80; // 高版本安卓用WebP格式获得更高压缩率,低版本回退JPEG Bitmap.CompressFormat compressFormat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ? Bitmap.CompressFormat.WEBP_LOSSY : Bitmap.CompressFormat.JPEG; sampledBitmap.compress(compressFormat, quality, compressOut); while (compressOut.toByteArray().length / 1024 > targetSizeKB && quality > 30) { compressOut.reset(); quality -= 5; sampledBitmap.compress(compressFormat, quality, compressOut); } // 回收位图释放内存 sampledBitmap.recycle(); return new ByteArrayInputStream(compressOut.toByteArray()); }
替换原有的上传逻辑,将原始流替换为压缩后的流:
try { // 压缩图片,目标体积设为120KB InputStream compressedStream = compressImage(this, uri, 120); Amplify.Storage.uploadInputStream( editText.getText().toString(), compressedStream, StorageUploadInputStreamOptions.defaultInstance(), this::showProgress, result -> { // 上传完成关闭流 try { compressedStream.close(); } catch (IOException e) { e.printStackTrace(); } uploded(result); }, failure -> { // 上传失败也要关闭流 try { compressedStream.close(); } catch (IOException e) { e.printStackTrace(); } Log.e("MyAmplifyApp", "Upload failed", failure); } ); } catch (FileNotFoundException error) { Log.e("MyAmplifyApp", "Could not find file to open for input stream.", error); } catch (IOException e) { Log.e("MyAmplifyApp", "File compress failed", e); }
落地注意事项:
- 所有压缩、解码操作禁止在主线程执行,否则会触发ANR
- 批量上传场景可以将压缩+上传逻辑放到前台服务中执行,避免应用退后台后进程被系统杀掉中断任务
- 优先在客户端完成压缩,相比服务端压缩的方案,能大幅减少上传流量消耗、缩短上传等待时间,整体成本更低
内容的提问来源于stack exchange,提问作者ThinQ
相关产品推荐
相关产品推荐

