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

Android模拟器相册图片上传时出现ENOENT目录不存在错误求助

解决Android模拟器相册图片上传服务器的ENOENT文件不存在错误

错误日志:D/Main: /document/1AE6-2C19:Download/food2.jpg: open failed: ENOENT (No such file or directory)
在Android模拟器中从相册选取图片上传至服务器时触发该错误,已尝试Stack Overflow同类问题解决方案但未解决。

问题根源

你直接把Content URI的字符串当成真实文件路径来创建RequestBody了。相册返回的content://开头的URI是系统提供的内容访问标识,并非文件系统中的真实路径,用这个字符串创建请求体必然导致系统找不到对应文件,抛出ENOENT错误。

修正方案

一、重构上传逻辑,通过ContentResolver读取文件

放弃直接使用URI字符串,改为通过ContentResolver打开文件输入流,将流转换为RequestBody。修改uploadProfileImage方法及添加辅助工具方法:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) {
        // 直接保留Uri对象,不要转成字符串
        profileImageUri = data.getData();
        try {
            Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), profileImageUri);
            profilePicture.setImageBitmap(bitmap);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public void uploadProfileImage(){
    if (profileImageUri == null) {
        Toast.makeText(this, "请先选择图片", Toast.LENGTH_SHORT).show();
        return;
    }

    try {
        // 获取文件MIME类型,没有则默认设为JPEG
        String mimeType = getContentResolver().getType(profileImageUri);
        mimeType = mimeType == null ? "image/jpeg" : mimeType;

        // 通过ContentResolver读取文件字节流,创建RequestBody
        RequestBody requestBody = RequestBody.create(
                MediaType.parse(mimeType),
                getBytesFromUri(profileImageUri)
        );

        // 从Uri中提取原文件名
        String fileName = getFileNameFromUri(profileImageUri);

        MultipartBody.Part body = MultipartBody.Part.createFormData("file", fileName, requestBody);

        Call<ImageDto> imageDtoCall = BaseUrlClient
                .getInstance().getApi().uploadProfileImage(body);
        imageDtoCall.enqueue(new Callback<ImageDto>() {
            @RequiresApi(api = Build.VERSION_CODES.O)
            @Override
            public void onResponse(Call<ImageDto> call, Response<ImageDto> response) {
                if (response.code() == 201){
                    assert response.body() != null;
                    Log.d("Main","The image url is "+response.body().getDownloadUrl());
                    //createPatient(response.body().getDownloadUrl());
                }else {
                    Log.d("Main","Response code is "+response.code());
                    Toast.makeText(PatientRegistrationActivity.this, "图片上传失败,请重试", Toast.LENGTH_SHORT).show();
                    try {
                        Log.d("Main",response.errorBody().string());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onFailure(Call<ImageDto> call, Throwable t) {
                Log.d("Main",t.getMessage());
                Toast.makeText(PatientRegistrationActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "读取图片失败", Toast.LENGTH_SHORT).show();
    }
}

// 将Uri对应的文件转为字节数组
private byte[] getBytesFromUri(Uri uri) throws IOException {
    InputStream inputStream = getContentResolver().openInputStream(uri);
    ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();

    int bufferSize = 1024;
    byte[] buffer = new byte[bufferSize];
    int len;
    while ((len = inputStream.read(buffer)) != -1) {
        byteBuffer.write(buffer, 0, len);
    }

    inputStream.close();
    return byteBuffer.toByteArray();
}

// 从Uri中提取文件名
private String getFileNameFromUri(Uri uri) {
    String fileName = "profile_image.jpg";
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    if (cursor != null && cursor.moveToFirst()) {
        int nameIndex = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME);
        if (nameIndex != -1) {
            fileName = cursor.getString(nameIndex);
        }
        cursor.close();
    }
    return fileName;
}

二、确认权限配置

在AndroidManifest.xml中添加必要权限,并在运行时动态申请:

<!-- Android 13及以上读取图片权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- Android 13以下版本读取存储权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

三、额外检查点

  • 确保模拟器的虚拟存储配置正常,相册中的测试文件确实存在
  • 避免在任何环节将Uri转为字符串存储,全程保留Uri对象进行操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 03:50:19