From 3c3bcc4ee423fa74bce95ac5d1dc57350e2b2465 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Mon, 1 Dec 2025 02:07:29 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E8=AF=95=E5=88=B0=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E6=8C=89=E9=92=AE=E5=88=B0=E5=89=AA=E8=A3=81?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E9=98=B6=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 +- .../BackgroundSettingsActivity.java | 384 +++++++++++------- .../studio/powerbell/utils/FileUtils.java | 30 +- 3 files changed, 257 insertions(+), 161 deletions(-) diff --git a/powerbell/build.properties b/powerbell/build.properties index cc4aea80..82ce9bcf 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sun Nov 30 17:13:20 GMT 2025 +#Sun Nov 30 18:04:35 GMT 2025 stageCount=13 libraryProject= baseVersion=15.11 publishVersion=15.11.12 -buildCount=7 +buildCount=13 baseBetaVersion=15.11.13 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java index eb59c9e8..0ea69ddd 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java @@ -37,6 +37,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import androidx.core.util.Preconditions; +import cc.winboll.studio.powerbell.BuildConfig; +import android.os.Environment; +import android.provider.Settings; +import androidx.appcompat.app.AlertDialog; +import android.content.DialogInterface; public class BackgroundSettingsActivity extends WinBoLLActivity implements BackgroundPicturePreviewDialog.IOnRecivedPictureListener { @@ -50,7 +55,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg private static final int STORAGE_PERMISSION_REQUEST = 100; // FileProvider 授权(必须与AndroidManifest.xml中配置一致) - private static final String FILE_PROVIDER_AUTHORITY = "cc.winboll.studio.powerbell.fileprovider"; + private static final String FILE_PROVIDER_AUTHORITY = BuildConfig.APPLICATION_ID + ".fileprovider"; private AToolbar mAToolbar; private File mfBackgroundDir; // 背景图片存储文件夹 @@ -184,14 +189,79 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg // 修复:选择图片后添加视图刷新逻辑 private View.OnClickListener onSelectPictureClickListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - if (checkAndRequestStoragePermission()) { - Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); - startActivityForResult(intent, REQUEST_SELECT_PICTURE); - } - } - }; + @Override + public void onClick(View v) { + if (checkAndRequestStoragePermission()) { + // 核心修复:创建多个意图作为兜底 + Intent[] intents = new Intent[3]; + + // 意图1:ACTION_GET_CONTENT(优先) + Intent getContentIntent = new Intent(Intent.ACTION_GET_CONTENT); + getContentIntent.setType("image/*"); + getContentIntent.addCategory(Intent.CATEGORY_OPENABLE); + getContentIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intents[0] = getContentIntent; + + // 意图2:ACTION_PICK(兜底) + Intent pickIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + pickIntent.setType("image/*"); + pickIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intents[1] = pickIntent; + + // 意图3:ACTION_OPEN_DOCUMENT(Android 4.4+) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + Intent openDocIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + openDocIntent.setType("image/*"); + openDocIntent.addCategory(Intent.CATEGORY_OPENABLE); + openDocIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + intents[2] = openDocIntent; + } + + // 遍历意图,找到第一个能响应的 + Intent validIntent = null; + for (Intent intent : intents) { + if (intent != null && intent.resolveActivity(getPackageManager()) != null) { + validIntent = intent; + break; + } + } + + // 创建chooser时添加flags + if (validIntent != null) { + Intent chooser = Intent.createChooser(validIntent, "选择图片"); + // 核心修复:传递持久化权限flag + chooser.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + startActivityForResult(chooser, REQUEST_SELECT_PICTURE); + } else { + // 确保对话框能正常显示 + runOnUiThread(new Runnable() { + @Override + public void run() { + ToastUtils.show("请安装相册应用,或点击确定下载系统相册"); + new AlertDialog.Builder(BackgroundSettingsActivity.this) + .setTitle("无图片选择应用") + .setMessage("需要安装相册应用才能选择图片") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent marketIntent = new Intent(Intent.ACTION_VIEW); + marketIntent.setData(Uri.parse("market://details?id=com.android.gallery3d")); + // 确保市场意图能响应 + if (marketIntent.resolveActivity(getPackageManager()) != null) { + startActivity(marketIntent); + } else { + ToastUtils.show("无法打开应用商店"); + } + } + }) + .setNegativeButton("取消", null) + .show(); + } + }); + } + } + } + }; private View.OnClickListener onCropPictureClickListener = new View.OnClickListener() { @Override @@ -353,9 +423,12 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg if (_mSourceCropTempFile.exists()) { _mSourceCropTempFile.delete(); } - + try { _mSourceCropTempFile.createNewFile(); + // 核心优化:设置文件权限 + _mSourceCropTempFile.setReadable(true, false); + _mSourceCropTempFile.setWritable(true, false); } catch (IOException e) { LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); ToastUtils.show("剪裁临时文件创建失败"); @@ -375,7 +448,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg // 核心修复2:裁剪意图兼容(适配不同机型) Intent intent = new Intent("com.android.camera.action.CROP"); // 兼容部分机型不支持隐式意图,添加包名过滤(可选,按需添加) - intent.setPackage("com.android.camera"); + //intent.setPackage("com.android.camera"); intent.setDataAndType(inputUri, "image/" + _mszCommonFileType); intent.putExtra("crop", "true"); intent.putExtra("noFaceDetection", true); @@ -437,19 +510,25 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg * 工具方法:生成Content Uri(适配Android 7.0+),需在AndroidManifest.xml中配置FileProvider */ private Uri getUriForFile(Context context, File file) throws Exception { + String targetPackage = getPackageName(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { try { - // 与AndroidManifest.xml中配置的FileProvider授权一致 - return FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, file); + Uri uri = FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, file); + // 显式授予Uri权限给目标应用(如裁剪工具的包名) + if (!TextUtils.isEmpty(targetPackage)) { + context.grantUriPermission(targetPackage, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } + return uri; } catch (Exception e) { LogUtils.e(TAG, "FileProvider生成Uri失败:" + e.getMessage() + ",文件路径:" + file.getPath()); - throw e; // 抛出异常,让上层处理 + throw e; } } else { - return Uri.fromFile(file); // 低版本兼容 + return Uri.fromFile(file); } } + /** * 保存剪裁后的Bitmap(彻底修复:路径拼接+权限+解析异常) */ @@ -628,142 +707,143 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg } LogUtils.d(TAG, "选择图片Uri : " + selectedImage.toString()); - // 核心修复1:替换路径解析方式,兼容UriUtil解析失败场景 + // 核心修复:对ACTION_GET_CONTENT返回的Uri,添加持久化权限(Android 4.4+) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + getContentResolver().takePersistableUriPermission( + selectedImage, + Intent.FLAG_GRANT_READ_URI_PERMISSION + ); + } + + // 路径解析逻辑保持不变... File fSrcImage = null; String filePath = UriUtil.getFilePathFromUri(this, selectedImage); if (!TextUtils.isEmpty(filePath)) { fSrcImage = new File(filePath); } else { - // 兼容方案:通过ContentResolver读取图片流,保存为临时文件 fSrcImage = new File(mfPictureDir, "selected_temp.jpg"); if (fSrcImage.exists()) { fSrcImage.delete(); } - // 复制图片流到临时文件 FileUtils.copyStreamToFile(getContentResolver().openInputStream(selectedImage), fSrcImage); LogUtils.d(TAG, "Uri解析失败,通过流复制生成临时文件:" + fSrcImage.getPath()); } - // 核心修复2:增强文件有效性校验 if (fSrcImage == null || !fSrcImage.exists() || fSrcImage.length() <= 0) { ToastUtils.show("选择的图片文件不存在或损坏"); return; } BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this); - // 保存图片到预览Bean并刷新 utils.saveFileToPreviewBean(fSrcImage, selectedImage.toString()); bvPreviewBackground.reloadPreviewBackground(); - - // 启动裁剪(增加异常捕获) startCropImageActivity(false); } catch (Exception e) { LogUtils.e(TAG, "选择图片异常" + e); ToastUtils.show("选择图片失败:" + e.getMessage()); - // 异常时清理临时文件 if (_mSourceCropTempFile.exists()) { _mSourceCropTempFile.delete(); LogUtils.d(TAG, "选择图片异常,清理临时文件:" + _mSourceCropTempFile.getPath()); } } } else if (requestCode == REQUEST_TAKE_PHOTO && resultCode == RESULT_OK) { - LogUtils.d(TAG, "REQUEST_TAKE_PHOTO"); - // 检查拍照文件是否有效 - if (!mfTakePhoto.exists() || mfTakePhoto.length() <= 0) { - ToastUtils.show("拍照文件不存在或损坏"); - // 清理临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - LogUtils.d(TAG, "拍照文件无效,清理临时文件"); - } - return; - } + LogUtils.d(TAG, "REQUEST_TAKE_PHOTO"); + // 检查拍照文件是否有效 + if (!mfTakePhoto.exists() || mfTakePhoto.length() <= 0) { + ToastUtils.show("拍照文件不存在或损坏"); + // 清理临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + LogUtils.d(TAG, "拍照文件无效,清理临时文件"); + } + return; + } - Bundle extras = data.getExtras(); - if (extras != null) { - Bitmap imageBitmap = (Bitmap) extras.get("data"); - if (imageBitmap != null && !imageBitmap.isRecycled()) { - compressQualityToRecivedPicture(imageBitmap); - // 拍照压缩后刷新预览 - bvPreviewBackground.reloadPreviewBackground(); - // 启动裁剪 - startCropImageActivity(false); - } else { - ToastUtils.show("拍照图片为空"); - // 清理临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - LogUtils.d(TAG, "拍照图片为空,清理临时文件:" + _mSourceCropTempFile.getPath()); - } - } - } else { - ToastUtils.show("拍照数据获取失败"); - // 清理临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - LogUtils.d(TAG, "拍照数据获取失败,清理临时文件:" + _mSourceCropTempFile.getPath()); - } - } - } else if (requestCode == REQUEST_CROP_IMAGE && resultCode == RESULT_OK) { - LogUtils.d(TAG, "CROP_IMAGE_REQUEST_CODE"); - try { - Bitmap cropBitmap = null; - // 核心修复:优先读取裁剪临时文件(放弃data.getParcelableExtra,避免缩略图) - if (_mSourceCropTempFile.exists() && _mSourceCropTempFile.length() > 0) { - LogUtils.d(TAG, String.format("_mSourceCropTempFile 信息:路径=%s , 大小=%d bytes", + Bundle extras = data.getExtras(); + if (extras != null) { + Bitmap imageBitmap = (Bitmap) extras.get("data"); + if (imageBitmap != null && !imageBitmap.isRecycled()) { + compressQualityToRecivedPicture(imageBitmap); + // 拍照压缩后刷新预览 + bvPreviewBackground.reloadPreviewBackground(); + // 启动裁剪 + startCropImageActivity(false); + } else { + ToastUtils.show("拍照图片为空"); + // 清理临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + LogUtils.d(TAG, "拍照图片为空,清理临时文件:" + _mSourceCropTempFile.getPath()); + } + } + } else { + ToastUtils.show("拍照数据获取失败"); + // 清理临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + LogUtils.d(TAG, "拍照数据获取失败,清理临时文件:" + _mSourceCropTempFile.getPath()); + } + } + } else if (requestCode == REQUEST_CROP_IMAGE && resultCode == RESULT_OK) { + LogUtils.d(TAG, "CROP_IMAGE_REQUEST_CODE"); + try { + Bitmap cropBitmap = null; + // 核心修复:优先读取裁剪临时文件(放弃data.getParcelableExtra,避免缩略图) + if (_mSourceCropTempFile.exists() && _mSourceCropTempFile.length() > 0) { + LogUtils.d(TAG, String.format("_mSourceCropTempFile 信息:路径=%s , 大小=%d bytes", _mSourceCropTempFile.getPath(), _mSourceCropTempFile.length())); - // 优化Bitmap解析选项(避免OOM和损坏图片解析失败) - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.RGB_565; // 省内存(仅RGB,无透明通道) - options.inSampleSize = 1; // 不缩放(保证清晰度) - options.inJustDecodeBounds = false; - cropBitmap = BitmapFactory.decodeFile(_mSourceCropTempFile.getPath(), options); - } else { - ToastUtils.show("剪裁文件为空或损坏"); - return; - } + // 优化Bitmap解析选项(避免OOM和损坏图片解析失败) + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.RGB_565; // 省内存(仅RGB,无透明通道) + options.inSampleSize = 1; // 不缩放(保证清晰度) + options.inJustDecodeBounds = false; + cropBitmap = BitmapFactory.decodeFile(_mSourceCropTempFile.getPath(), options); + } else { + ToastUtils.show("剪裁文件为空或损坏"); + return; + } - // 检查解析后的Bitmap是否有效 - if (cropBitmap != null && !cropBitmap.isRecycled()) { - saveCropBitmap(cropBitmap); // 调用保存方法(内含修复逻辑) - } else { - ToastUtils.show("获取剪裁图片失败(Bitmap解析异常)"); - // 清理无效临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - LogUtils.d(TAG, "Bitmap解析失败,清理无效临时文件"); - } - } - } catch (OutOfMemoryError e) { - LogUtils.e(TAG, "内存溢出" + e); - ToastUtils.show("保存失败:内存不足,请尝试裁剪更小的图片"); - // 清理临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - } - } catch (Exception e) { - LogUtils.e(TAG, "剪裁保存异常" + e); - ToastUtils.show("保存失败:" + e.getMessage()); - // 清理临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - } - } finally { - // 裁剪流程结束,强制清理临时文件(双重保障,避免残留) - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - LogUtils.d(TAG, "裁剪流程结束,强制清理临时文件:" + _mSourceCropTempFile.getPath()); - } - } - } else if (resultCode != RESULT_OK) { - LogUtils.d(TAG, "操作取消或失败,requestCode: " + requestCode); - ToastUtils.show("操作已取消"); - // 操作取消/失败时,强制清理临时文件 - if (_mSourceCropTempFile.exists()) { - _mSourceCropTempFile.delete(); - LogUtils.d(TAG, "操作取消/失败,清理临时文件:" + _mSourceCropTempFile.getPath()); - } - } + // 检查解析后的Bitmap是否有效 + if (cropBitmap != null && !cropBitmap.isRecycled()) { + saveCropBitmap(cropBitmap); // 调用保存方法(内含修复逻辑) + } else { + ToastUtils.show("获取剪裁图片失败(Bitmap解析异常)"); + // 清理无效临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + LogUtils.d(TAG, "Bitmap解析失败,清理无效临时文件"); + } + } + } catch (OutOfMemoryError e) { + LogUtils.e(TAG, "内存溢出" + e); + ToastUtils.show("保存失败:内存不足,请尝试裁剪更小的图片"); + // 清理临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + } + } catch (Exception e) { + LogUtils.e(TAG, "剪裁保存异常" + e); + ToastUtils.show("保存失败:" + e.getMessage()); + // 清理临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + } + } finally { + // 裁剪流程结束,强制清理临时文件(双重保障,避免残留) + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + LogUtils.d(TAG, "裁剪流程结束,强制清理临时文件:" + _mSourceCropTempFile.getPath()); + } + } + } else if (resultCode != RESULT_OK) { + LogUtils.d(TAG, "操作取消或失败,requestCode: " + requestCode); + ToastUtils.show("操作已取消"); + // 操作取消/失败时,强制清理临时文件 + if (_mSourceCropTempFile.exists()) { + _mSourceCropTempFile.delete(); + LogUtils.d(TAG, "操作取消/失败,清理临时文件:" + _mSourceCropTempFile.getPath()); + } + } } /** @@ -782,37 +862,47 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg * 检查并申请存储权限(修复:适配低版本API,移除Android13+依赖) */ private boolean checkAndRequestStoragePermission() { - // 仅保留Android 6.0+(API 23)的WRITE_EXTERNAL_STORAGE权限,兼容低版本 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // M = API 23,Android 6.0 - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, - new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - STORAGE_PERMISSION_REQUEST); - return false; - } - } - return true; - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (!Environment.isExternalStorageManager()) { + Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); + startActivity(intent); + return false; + } + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + // 核心修复:同时申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE + String[] permissions = new String[]{ + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED + || ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, permissions, STORAGE_PERMISSION_REQUEST); + return false; + } + } + return true; + } - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == STORAGE_PERMISSION_REQUEST) { - boolean isGranted = false; - // 检查权限是否授予 - for (int result : grantResults) { - if (result == PackageManager.PERMISSION_GRANTED) { - isGranted = true; - break; - } - } - if (isGranted) { - ToastUtils.show("存储权限已获取"); - } else { - ToastUtils.show("需要存储权限才能保存/选择图片"); - } - } - } + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == STORAGE_PERMISSION_REQUEST) { + boolean isGranted = false; + for (int result : grantResults) { + if (result == PackageManager.PERMISSION_GRANTED) { + isGranted = true; + break; + } + } + if (isGranted) { + ToastUtils.show("存储权限已获取,正在打开图片选择器"); + // 核心优化:自动重试图片选择 + onSelectPictureClickListener.onClick(findViewById(R.id.activitybackgroundpictureAButton2)); + } else { + ToastUtils.show("需要存储权限才能保存/选择图片"); + } + } + } void setBackgroundColor() { BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this); diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/FileUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/FileUtils.java index 1d28e620..0aad1cee 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/FileUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/FileUtils.java @@ -238,22 +238,28 @@ public class FileUtils { */ public static void copyStreamToFile(InputStream inputStream, File file) throws IOException { if (inputStream == null || file == null) { - return; + throw new IllegalArgumentException("InputStream或File不能为空"); } - // 确保父目录存在 File parentDir = file.getParentFile(); - if (!parentDir.exists()) { - parentDir.mkdirs(); + if (!parentDir.exists() && !parentDir.mkdirs()) { + throw new IOException("无法创建父目录:" + parentDir.getAbsolutePath()); } - FileOutputStream fos = new FileOutputStream(file); - byte[] buffer = new byte[1024]; - int len; - while ((len = inputStream.read(buffer)) != -1) { - fos.write(buffer, 0, len); + try { + OutputStream outputStream = new FileOutputStream(file); + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, length); + } + outputStream.flush(); + } finally { + try { + inputStream.close(); + } catch (IOException e) { + LogUtils.e("FileUtils", "关闭输入流失败:" + e.getMessage()); + } } - fos.flush(); - fos.close(); - inputStream.close(); } + }