使用Retrofit上传File类型图片至数据库时出现文件路径不存在错误
图片上传失败(ENOENT错误)排查与解决
问题描述
处理用户资料Fragment时,点击按钮能正常显示图片,但调用uploadImage方法上传时进入onFailure回调,报错信息:
12006-12006 TAG com.example.sts_passenger I onFailure: /storage/emulated/0/Screenshot_20230523-073140_WhatsApp.jpg: open failed: ENOENT (No such file or directory)
上传方法代码:
public void uploadImage(File file){ // Create a request body with File(file) RequestBody requestBody = RequestBody.create(MediaType.parse("image/*"), file); // Create a MultipartBody.Part from the request body MultipartBody.Part photo = MultipartBody.Part.createFormData("photo", file.getName(), requestBody); Log.i("TAG", "uploadImage: file name " + file.getName()); // Api call Call<ResponseBody> req = Client.getInstance(Consts.BASE_URL_PASSENGER_AUTH).getRoute().uploadProfilePhoto(photo, session.getToken(), session.getPassenger().getPassengerId()); Log.i("TAG", "uploadImage: enqueueing " + photo + " " + session.getPassenger().getPassengerId()); req.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccessful()) { Log.i("TAG", "onResponse: image uploaded" ); Log.i("TAG", "onResponse: success file name " + photo); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.i("TAG", "onFailure: " + t.getLocalizedMessage()); } }); }
API接口定义:
@Multipart @POST(Consts.ENDPOINT_UPLOAD_PROFILE_PIC) Call<ResponseBody> uploadProfilePhoto( @Part MultipartBody.Part photo, @Header("Authorization") String userSessionToken, @Path("passenger_id") Integer passengerId);
原因排查
- 文件路径无效或文件不存在:报错明确指向文件找不到,虽然能显示图片,但可能显示时使用的是Uri而非真实文件路径,或文件在选择后被移动/删除,导致传入
uploadImage的File对象路径失效。 - 分区存储权限限制:Android 10+采用分区存储机制,直接访问外部存储根目录文件可能无读取权限,即使能显示图片,上传时读取文件会被系统拒绝,表现为文件找不到的错误。
- API路径参数不匹配:接口使用
@Path("passenger_id"),但Consts.ENDPOINT_UPLOAD_PROFILE_PIC可能未包含{passenger_id}占位符,导致请求路径错误(当前报错为文件问题,此为次要排查点)。
解决方法
验证文件真实状态
在uploadImage方法开头添加文件存在性与可读性检查:if (!file.exists()) { Log.e("TAG", "uploadImage: File not found at path: " + file.getAbsolutePath()); return; } if (!file.canRead()) { Log.e("TAG", "uploadImage: No read permission for file: " + file.getAbsolutePath()); return; }若检查不通过,需重新获取正确的文件路径(比如从选择图片的Uri转换为真实路径,或通过ContentResolver读取文件流)。
适配Android分区存储
- 对于Android 10及以上版本,避免直接使用File对象访问外部存储文件,改用
ContentResolver读取文件流并转为RequestBody:// 假设imageUri是从相册选择得到的图片Uri InputStream inputStream = getContext().getContentResolver().openInputStream(imageUri); byte[] imageBytes = new byte[inputStream.available()]; inputStream.read(imageBytes); RequestBody requestBody = RequestBody.create(MediaType.parse("image/*"), imageBytes); MultipartBody.Part photo = MultipartBody.Part.createFormData("photo", fileName, requestBody); - 确保已申请对应权限:Android 13+需申请
READ_MEDIA_IMAGES,低版本需READ_EXTERNAL_STORAGE,并完成运行时动态权限申请。
- 对于Android 10及以上版本,避免直接使用File对象访问外部存储文件,改用
检查API路径配置
确认Consts.ENDPOINT_UPLOAD_PROFILE_PIC包含{passenger_id}占位符,例如:public static final String ENDPOINT_UPLOAD_PROFILE_PIC = "passengers/{passenger_id}/profile-photo";若缺少占位符,
@Path("passenger_id")无法完成路径替换,会导致请求失败。替换过时的OkHttp方法
旧版RequestBody.create(MediaType, File)已过时,建议改用:RequestBody requestBody = RequestBody.create(file, MediaType.parse("image/*"));或直接使用
MultipartBody.Part的重载方法:MultipartBody.Part photo = MultipartBody.Part.createFormData("photo", file.getName(), file);
内容的提问来源于stack exchange,提问作者dev.darron
相关产品推荐
相关产品推荐

