Android Kotlin: Getting a FileNotFoundException with filename chosen from file picker?

后端 未结 3 940
眼角桃花
眼角桃花 2020-11-22 01:13

I\'m working on an Android application where one of the features is to let the user choose a file to open (I\'m wanting to open a plain text .txt file). I\'ve worked on And

相关标签:
3条回答
  • 2020-11-22 01:32

    You cannot open a Java File on a ÙRI converted to a String, the "path" section of the URI has no relation to a physical file location.

    Use a contentResolver to get a Java FileDescriptor to open the file with.

    val parcelFileDescriptor: ParcelFileDescriptor =
                contentResolver.openFileDescriptor(uri, "r")
        val fileDescriptor: FileDescriptor = parcelFileDescriptor.fileDescriptor
    

    This method is compatible with Android 10 where file paths for non App private directories are not usuable.

    https://developer.android.com/training/data-storage/shared/documents-files

    0 讨论(0)
  • 2020-11-22 01:39

    You did not receive a file path, you received a Uri. You have to use Uri based APIs such as ContentResolver.openInputStream() to access the contents at that Uri as Android does not grant your app direct File access to the underlying file (it could also be streamed from Google Drive or downloaded directly from the internet without your app being aware that this is happening):

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
    
        // Selected a file to load
        if ((requestCode == 111) && (resultCode == RESULT_OK)) {
            val selectedFilename = data?.data //The uri with the location of the file
            if (selectedFilename != null) {
                contentResolver.openInputStream(selectedFilename)?.bufferedReader()?.forEachLine {
                    val toast = Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT)
                    toast.show()
                }
            } else {
                val msg = "Null filename data received!"
                val toast = Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG)
                toast.show()
            }
        }
    }
    

    Here we can assume we get contents of the proper format by passing in the proper mime type to our request (as there is no requirement that a text file end in exactly the .txt extension as part of its path):

    val intent = Intent()
        .setType("text/*")
        .setAction(Intent.ACTION_GET_CONTENT)
    
    startActivityForResult(Intent.createChooser(intent, "Select a file"), 111)
    

    Which will automatically make any non text file unable to be selected.

    0 讨论(0)
  • 2020-11-22 01:43

    If you are getting "msf:xxx" in URI, use below solution where I have created temp file in app cache directory and deleted same file after completion of my task:

    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;
                    }
    

    I have fixed this issue and it's working 100% for msf. :)

    Also Delete the temp file after the completion of your work:

    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();
                    }
                }
            }
        }
    

    Here TEMP_FILE value is "temp."

    0 讨论(0)
提交回复
热议问题