You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android基于Glide加载ImageView的URL图片 下载存外部存储报文件不存在错误

报错根因

你遇到的FileNotFoundException带ENOENT (No such file or directory)提示,核心是调用new FileOutputStream(outFile)时,目标文件的父目录实际不存在,代码没有对目录创建的结果做校验,默认mkdirs()一定能成功。
几个会直接触发这个报错的隐患:

  • 目录路径写法不兼容:直接拼接Environment.getExternalStorageDirectory()的路径,在Android 10以上版本因为分区存储限制,没有对应写入权限,mkdirs()会静默返回失败,不会抛异常,代码后续直接在不存在的目录下创建文件流,自然报错。
  • 权限逻辑有漏洞:用户首次触发权限申请时,checkPermission直接返回false,弹框让用户授权后,没有在权限回调里执行下载逻辑,就算用户最后同意权限,本次点击的下载流程也不会继续。
  • 流程控制有缺陷:捕获到文件流创建的异常后,只打印堆栈没有中断后续逻辑,继续执行图片压缩、流操作,会额外触发空指针崩溃。
  • 从ImageView拿Bitmap的逻辑有崩溃风险:Glide加载完成后可能为ImageView设置带动画、圆角效果的自定义Drawable,直接强转成BitmapDrawable会触发类转换异常,根本走不到文件写入步骤。
可落地的修复方案

1. 修正目录创建与写入逻辑

替换原来的手动创建目录、直接拿文件流的代码,必须对目录创建结果做校验:

File sdCardRoot = Environment.getExternalStorageDirectory();
File dir = new File(sdCardRoot, "TirunelveliParithabangal");
// 必须校验目录是否真实存在,创建失败直接中断流程
if (!dir.exists() && !dir.mkdirs()) {
    TastyToast.makeText(getApplicationContext(), "保存目录创建失败", TastyToast.LENGTH_LONG, TastyToast.ERROR);
    return;
}
String fileName = String.format("%d.jpg", System.currentTimeMillis());
File outFile = new File(dir, fileName);
// 用try-with-resources自动管理流关闭,避免内存泄漏
try (FileOutputStream outStream = new FileOutputStream(outFile)) {
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
    outStream.flush();
    TastyToast.makeText(getApplicationContext(), "Meme Downloaded Successfully", TastyToast.LENGTH_LONG*6, TastyToast.SUCCESS);
} catch (IOException e) {
    e.printStackTrace();
    TastyToast.makeText(getApplicationContext(), "图片保存失败", TastyToast.LENGTH_LONG, TastyToast.ERROR);
}

如果你的应用targetSdkVersion >= 29,不要继续用直接操作外部存储路径的方式,改用MediaStore接口写入公共图片目录,不需要额外申请存储权限,完全适配分区存储规则:

ContentValues fileValues = new ContentValues();
fileValues.put(MediaStore.Images.Media.DISPLAY_NAME, String.format("%d.jpg", System.currentTimeMillis()));
fileValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
fileValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/TirunelveliParithabangal");
Uri fileUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, fileValues);
try (OutputStream outStream = getContentResolver().openOutputStream(fileUri)) {
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
}

2. 替换Bitmap获取方式,避免强转异常

不要直接从meme_Image_view里强转拿Bitmap,直接用Glide的同步缓存读取接口拿原始Bitmap,清晰度更高也不会崩溃:

// 注意:这段文件IO、Glide同步加载的逻辑必须放在子线程执行,不能在主线程跑
Bitmap saveBitmap = Glide.with(this)
        .asBitmap()
        .load(url)
        .submit()
        .get();

因为你配置了DiskCacheStrategy.ALL,图片已经被Glide本地缓存,同步读取几乎不会有额外网络耗时。

3. 补全权限流程的逻辑漏洞

原来的权限判断只做了单次校验,没有处理用户授权后的回调:

  • 当权限被拒绝、弹框引导用户申请权限后,要重写onRequestPermissionsResult方法,在用户同意对应权限的回调分支里,重新执行下载逻辑
  • Android 10以上如果还想沿用旧的存储写入逻辑,需要在AndroidManifest的application节点下配置android:requestLegacyExternalStorage="true",否则就算申请了WRITE_EXTERNAL_STORAGE权限,也无法写入公共外部目录

内容的提问来源于stack exchange,提问作者novaw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.23 04:15:05