Android Kotlin:获取文件名从文件选择器中选择的FileNotFoundException?

Android Kotlin:获取文件名从文件选择器中选择的FileNotFoundException?,android,file,exception,kotlin,filenotfoundexception,Android,File,Exception,Kotlin,Filenotfoundexception,我正在开发一个Android应用程序,其中一个功能是让用户选择要打开的文件(我想打开一个纯文本.txt文件)。我以前使用Java开发过Android应用程序,但这次我使用了Kotlin,这是我第一次使用Kotlin 我目前让应用程序显示一个文件选择器,让用户点击他们想要打开的文件。然后我尝试使用一个File对象来打开文件并执行forEachLine循环。但出于某种原因,它会抛出一个java.io.FileNotFoundException(没有这样的文件或目录),该文件是从文件选择器中选择的。我

我正在开发一个Android应用程序,其中一个功能是让用户选择要打开的文件(我想打开一个纯文本.txt文件)。我以前使用Java开发过Android应用程序,但这次我使用了Kotlin,这是我第一次使用Kotlin

我目前让应用程序显示一个文件选择器,让用户点击他们想要打开的文件。然后我尝试使用一个File对象来打开文件并执行forEachLine循环。但出于某种原因,它会抛出一个java.io.FileNotFoundException(没有这样的文件或目录),该文件是从文件选择器中选择的。我不确定出了什么问题,如果我必须做一些转换来转换文件路径

“加载”按钮的代码:

val btn_load: Button = findViewById<Button>(R.id.btn_load_puzzle)
    btn_load.setOnClickListener {
        val intent = Intent()
            .setType("*/*")
            .setAction(Intent.ACTION_GET_CONTENT)

        startActivityForResult(Intent.createChooser(intent, "Select a file"), 111)
    }
FileNotFound异常在创建File对象以执行forEachLine循环的行上抛出:

java.lang.RuntimeException:未能传递结果ResultInfo{who=null,request=111,result=-1,data=Intent{dat=content://com.android.externalstorage.documents/document/0000-0000:Sudoku 拼图/hard001.txt flg=0x1}}到活动{com.example.sudokusolver/com.example.sudokusolver.MainActivity}:java.io.FileNotFoundException:/document/0000-0000:Sudoku-puzzles/hard001.txt(无此类文件或目录)


您没有收到文件路径,而是收到了一个
Uri
。您必须使用基于
Uri
的API,例如访问
Uri
的内容,因为Android不会授予您的应用程序直接访问底层文件的权限(它也可以从Google Drive流式传输或直接从internet下载,而您的应用程序不知道发生了这种情况):

在这里,我们可以假设通过向请求传递适当的mime类型来获得适当格式的内容(因为不要求文本文件以
.txt
扩展名结尾作为其路径的一部分):


这将自动使任何非文本文件无法被选择。

您无法在转换为字符串的
ÙRI
上打开Java文件,URI的“路径”部分与物理文件位置无关

使用
contentResolver
获取Java
FileDescriptor
以打开文件

val parcelFileDescriptor:parcelFileDescriptor=
openFileDescriptor(uri,“r”)
val fileDescriptor:fileDescriptor=parcelFileDescriptor.fileDescriptor
此方法与Android 10兼容,在Android 10中,非应用程序专用目录的文件路径不可用

如果您在URI中获得“msf:xxx”,请使用以下解决方案,其中我已在应用缓存目录中创建了临时文件,并在完成任务后删除了相同的文件:

if (id != null && id.startsWith("msf:")) {
                    final File file = new File(mContext.getCacheDir(), Constant.TEMP_FILE + Objects.requireNonNull(mContext.getContentResolver().getType(imageUri)).split("/")[1]);
                    try (final InputStream inputStream = mContext.getContentResolver().openInputStream(imageUri); OutputStream output = new FileOutputStream(file)) {
                        final byte[] buffer = new byte[4 * 1024]; // or other buffer size
                        int read;

                        while ((read = inputStream.read(buffer)) != -1) {
                            output.write(buffer, 0, read);
                        }

                        output.flush();
                        return file;
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                    return null;
                }
我已经解决了这个问题,它对无国界医生100%有效。:)

完成工作后,还要删除临时文件:

private void deleteTempFile() {
        final File[] files = requireContext().getCacheDir().listFiles();
        if (files != null) {
            for (final File file : files) {
                if (file.getName().contains(Constant.TEMP_FILE)) {
                    file.delete();
                }
            }
        }
    }

此处临时文件值为“TEMP”。

根据位图文件的URI打开位图文件:

private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    ParcelFileDescriptor parcelFileDescriptor =
            getContentResolver().openFileDescriptor(uri, "r");
    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    parcelFileDescriptor.close();
    return image;
}

它是否也适用于文档文件,如.pdf/.docx/.xls等?@NarendraSingh它也适用,但在上面的代码中我使用了缓存目录。请确保文件大小与此相符。对于较大的文件,我们可以使用文件目录,对吗?@SANAT我们可以获取文档的实际名称,而不是将其命名为我们的名称(如此处的“temp”)@arosis尝试从URI获取文件名:
if (id != null && id.startsWith("msf:")) {
                    final File file = new File(mContext.getCacheDir(), Constant.TEMP_FILE + Objects.requireNonNull(mContext.getContentResolver().getType(imageUri)).split("/")[1]);
                    try (final InputStream inputStream = mContext.getContentResolver().openInputStream(imageUri); OutputStream output = new FileOutputStream(file)) {
                        final byte[] buffer = new byte[4 * 1024]; // or other buffer size
                        int read;

                        while ((read = inputStream.read(buffer)) != -1) {
                            output.write(buffer, 0, read);
                        }

                        output.flush();
                        return file;
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                    return null;
                }
private void deleteTempFile() {
        final File[] files = requireContext().getCacheDir().listFiles();
        if (files != null) {
            for (final File file : files) {
                if (file.getName().contains(Constant.TEMP_FILE)) {
                    file.delete();
                }
            }
        }
    }
private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    ParcelFileDescriptor parcelFileDescriptor =
            getContentResolver().openFileDescriptor(uri, "r");
    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    parcelFileDescriptor.close();
    return image;
}