Android开发:Glide加载外部存储菜谱图片失败及上传方法问询
解决Glide加载外部存储图片报错&菜谱图片上传问题
嘿,我来帮你搞定这两个问题——先是Glide加载图片报EFAULT (Bad address)的奇怪错误,再是实现让你姑姑上传图片到指定目录的需求。
一、修复Glide加载外部图片的EFAULT错误
明明文件存在且能读,Glide却加载失败,这个Bad address错误大概率是路径解析或者权限配置的问题,给你几个靠谱的解决方向:
1. 别硬编码路径!用系统API拿正确路径
硬写/sdcard/真的容易踩坑,不同设备的外部存储路径可能不一样(比如有些是/storage/emulated/0/),Glide解析这种硬编码路径时可能出问题。换成系统提供的API来获取公共图片目录:
// 获取系统公共Pictures目录 File picturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); // 构建你的Recipes子目录 File recipesDir = new File(picturesDir, "Recipes"); // 确保目录存在,不存在就创建 if (!recipesDir.exists()) { recipesDir.mkdirs(); } // 生成正确的图片文件对象 File imageFile = new File(recipesDir, "carrot_cake.jpg");
2. 检查权限配置是否到位
就算文件能被检测到,权限没配好也可能导致Glide加载失败:
- 对于Android 10及以下,在
AndroidManifest.xml里加这两个权限:<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - 要是你用的是Android 10(API 29)及以上,且targetSdkVersion >=29,记得在
<application>标签里加这句兼容旧逻辑:
(如果targetSdkVersion >=30,更推荐用MediaStore管理公共目录文件,但临时用这个属性能快速解决问题)android:requestLegacyExternalStorage="true"
3. 给Glide传File对象而非字符串路径
直接传File对象给Glide,能减少路径字符串解析时的潜在问题,试试改代码:
if (imageFile.exists() && imageFile.canRead()) { Log.d(TAG, "File exists"); // 这里传imageFile而非字符串路径 Glide.with(context) .load(imageFile) .into(foodImage); }
二、实现图片上传到指定目录并通过Glide加载
你想让姑姑上传图片到/sdcard/Pictures/Recipes/,可以分两步来做:
1. 选择图片并复制到目标目录
先让用户从相册或相机选图,然后把选中的图片复制到你的Recipes目录里,还要生成唯一文件名避免重复:
// 假设你已经通过Intent拿到了选中图片的Uri(比如从相册选择) private void saveImageToRecipesDir(Uri selectedImageUri) { try { InputStream inputStream = getContentResolver().openInputStream(selectedImageUri); File recipesDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Recipes"); if (!recipesDir.exists()) { recipesDir.mkdirs(); } // 生成唯一文件名,用时间戳避免重复 String uniqueFileName = "recipe_" + System.currentTimeMillis() + ".jpg"; File targetFile = new File(recipesDir, uniqueFileName); // 复制图片内容到目标文件 OutputStream outputStream = new FileOutputStream(targetFile); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } // 关闭流 inputStream.close(); outputStream.close(); // 把这个uniqueFileName存到SQLite数据库里 saveRecipeInfoToDb(uniqueFileName, "胡萝卜蛋糕", ...); // 你的菜谱信息保存逻辑 // 用Glide加载刚保存的图片 Glide.with(this) .load(targetFile) .placeholder(R.drawable.default_recipe) // 加个占位图更友好 .error(R.drawable.load_error) // 加载失败显示错误图 .into(foodImage); } catch (IOException e) { e.printStackTrace(); // 这里可以给用户弹个Toast提示保存失败 } }
2. 从数据库读文件名加载图片
之后从SQLite取出文件名,按正确方式构建File对象,再用Glide加载:
// 从数据库获取对应菜谱的图片文件名 String imageFileName = getImageFileNameFromDb(recipeId); File recipesDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Recipes"); File imageFile = new File(recipesDir, imageFileName); if (imageFile.exists() && imageFile.canRead()) { Glide.with(context) .load(imageFile) .placeholder(R.drawable.default_recipe) .error(R.drawable.load_error) .into(foodImage); } else { // 文件不存在的话,显示默认图 foodImage.setImageResource(R.drawable.default_recipe); }
额外小提示
- 给Glide加占位图和错误图,用户体验会好很多
- Android 13及以上,读取公共图片需要申请
READ_MEDIA_IMAGES权限,代替原来的READ_EXTERNAL_STORAGE - 要是你的APP只需要自己的图片,也可以考虑用应用私有外部存储(
getExternalFilesDir(Environment.DIRECTORY_PICTURES)),这种方式不需要申请存储权限,更安全合规
内容的提问来源于stack exchange,提问作者HemmaRoyD
相关产品推荐
相关产品推荐

