使用Retrofit上传多张图片时抛出FileNotFoundException异常如何解决?
问题根因
- Uri转File逻辑错误:相册选择返回的图片Uri基本为
content://开头的ContentProvider共享Uri,不是本地文件的真实路径,直接调用Uri.getPath()得到的路径无法对应到本地磁盘文件,所以创建File对象时抛出FileNotFoundException。 - 单图选择场景缺失partNames赋值:当用户只选择单张图片时,代码仅向
imageUriList添加了Uri,没有同步给partNames添加对应元素,遍历转换时会出现索引越界异常。 - 未适配安卓存储权限变更:安卓10及以上版本禁止直接访问共享存储的文件路径,必须通过ContentResolver访问Uri对应的内容。
修复方案
第一步:修正单图选择时的partNames赋值
找到onActivityResult逻辑里的单图分支,添加partNames赋值:
} else if (result.getData().getData() != null) { imageUriList.add(result.getData().getData()); // 新增这行,给单图补充part名称 partNames.add("0"); }
第二步:重写convertUriToFIle方法,使用ContentResolver读取Uri内容
不需要把Uri转成File,直接通过输入流创建RequestBody,适配所有类型的Uri:
private List<MultipartBody.Part> convertUriToFIle(List<String> partNames, List<Uri> imageUriList) throws IOException { List<MultipartBody.Part> files = new ArrayList<>(); ContentResolver contentResolver = getContext().getContentResolver(); for (int i = 0; i < imageUriList.size(); i++) { Uri uri = imageUriList.get(i); // 获取文件名 String fileName = getFileNameFromUri(uri, contentResolver); // 直接通过ContentResolver打开输入流创建RequestBody RequestBody requestBody = RequestBody.create( MediaType.parse(contentResolver.getType(uri)), readBytesFromUri(uri, contentResolver) ); // 如果后台要求上传的file参数名是统一值比如"files[]",可以把partNames.get(i)改成固定值,根据后台要求调整 files.add(MultipartBody.Part.createFormData(partNames.get(i), fileName, requestBody)); } return files; } // 辅助方法:从Uri获取文件名 private String getFileNameFromUri(Uri uri, ContentResolver contentResolver) { String fileName = "image_" + System.currentTimeMillis() + ".jpg"; Cursor cursor = contentResolver.query(uri, null, null, null, null); if (cursor != null && cursor.moveToFirst()) { int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); if (nameIndex >= 0) { fileName = cursor.getString(nameIndex); } cursor.close(); } return fileName; } // 辅助方法:从Uri读取字节数组 private byte[] readBytesFromUri(Uri uri, ContentResolver contentResolver) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); InputStream inputStream = contentResolver.openInputStream(uri); byte[] buffer = new byte[1024 * 4]; int len; while ((len = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, len); } inputStream.close(); return outputStream.toByteArray(); }
第三步:检查权限配置
在AndroidManifest.xml中添加对应存储权限:
<!-- 安卓13及以上 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- 安卓12及以下 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
同时确保选择图片前已经动态申请了对应的存储权限。
内容的提问来源于stack exchange,提问作者Hooman Hooshyar
相关产品推荐
相关产品推荐

