You need to enable JavaScript to run this app. 使用Retrofit上传多张图片时抛出FileNotFoundException异常如何解决?
优惠活动
大模型
产品
解决方案
定价
更多

使用Retrofit上传多张图片时抛出FileNotFoundException异常如何解决?

问题根因

  1. Uri转File逻辑错误:相册选择返回的图片Uri基本为content://开头的ContentProvider共享Uri,不是本地文件的真实路径,直接调用Uri.getPath()得到的路径无法对应到本地磁盘文件,所以创建File对象时抛出FileNotFoundException。
  2. 单图选择场景缺失partNames赋值:当用户只选择单张图片时,代码仅向imageUriList添加了Uri,没有同步给partNames添加对应元素,遍历转换时会出现索引越界异常。
  3. 未适配安卓存储权限变更:安卓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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 11:36:03