三星设备文件上传报错FileNotFoundException:ENOENT无文件或目录求助
解决三星设备文件上传时的FileNotFoundException问题
兄弟,我之前在三星设备上踩过一模一样的坑!结合你给出的代码场景,咱们来一步步解决这个FileNotFoundException(错误信息open failed: ENOENT (No such file or directory))的问题:
问题根源
三星设备对Android的Scoped Storage(分区存储)机制实现得更严格,加上自家文件管理器的特殊处理,导致你通过Intent获取到的Uri并不是直接的本地文件路径——如果你强行把Uri解析成文件路径去访问,自然会找不到文件抛出错误。
解决方案
核心思路:放弃直接解析Uri为文件路径,改用ContentResolver读取文件内容,这是符合Android 10及以上版本规范的做法,也能完美适配三星设备。
1. 替换文件选择Intent为更可靠的ACTION_OPEN_DOCUMENT
把原来的Intent改成ACTION_OPEN_DOCUMENT,它能通过系统文件选择器授予临时文件访问权限,比旧的ACTION_GET_CONTENT更稳定:
button_upload_attachment.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Android 13+ 不需要READ_EXTERNAL_STORAGE权限,ACTION_OPEN_DOCUMENT会自动处理权限 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); // 允许选择任意类型文件 startActivityForResult(intent, REQUEST_CODE_FILE_PICK); // 自己定义REQUEST_CODE_FILE_PICK常量 } });
2. 通过ContentResolver读取文件内容并上传
在onActivityResult(或者用Jetpack的ActivityResultContracts)中,获取到Uri后,直接打开输入流读取文件内容,不要尝试转路径:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_FILE_PICK && resultCode == RESULT_OK && data != null) { Uri fileUri = data.getData(); if (fileUri == null) return; try { // 打开文件输入流 InputStream inputStream = getContentResolver().openInputStream(fileUri); if (inputStream == null) return; // 示例:用OkHttp上传文件(你可以替换成自己的上传逻辑) // 获取文件MIME类型和文件名 String mimeType = getContentResolver().getType(fileUri); String fileName = getFileName(fileUri); RequestBody fileBody = RequestBody.create(MediaType.parse(mimeType), inputStream); MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", fileName, fileBody); // 构建上传请求 OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("你的服务器上传地址") .post(MultipartBody.Builder() .setType(MultipartBody.FORM) .addPart(filePart) .build()) .build(); // 执行请求(建议用异步方式) client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); // 处理上传失败 } @Override public void onResponse(Call call, Response response) throws IOException { // 处理上传成功 if (response.isSuccessful()) { // 逻辑代码 } } }); } catch (FileNotFoundException e) { e.printStackTrace(); } } } // 辅助方法:从Uri获取文件名 private String getFileName(Uri uri) { String fileName = null; Cursor cursor = getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null); if (cursor != null && cursor.moveToFirst()) { fileName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); cursor.close(); } return fileName != null ? fileName : "unknown_file"; }
3. (可选)持久化文件访问权限
如果需要后续再次访问该文件,可以获取持久化权限:
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); getContentResolver().takePersistableUriPermission(fileUri, takeFlags);
额外注意点
- 如果你的App targetSdkVersion >=33,不需要申请
READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE权限,ACTION_OPEN_DOCUMENT会自动处理临时访问权限; - 不要尝试用第三方库强制解析Uri为文件路径,这种做法在三星设备上大概率失效,而且不符合Android规范。
内容的提问来源于stack exchange,提问作者Brinda Rathod
相关产品推荐
相关产品推荐

