'Android Kotlin: Getting a FileNotFoundException with filename chosen from file picker?
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 Android apps before with Java, but for this one, I'm using Kotlin, and it's my first time using Kotlin.
I currently have the app display a file chooser and let the user tap the file they want to open. Then I'm trying to use a File object to open the file and do a forEachLine loop. But for some reason, it's throwing a java.io.FileNotFoundException (No such file or directory) with the file chosen from the file picker. I'm not sure what's wrong, and if I have to do some conversion to convert the file path?
The code for my 'load' button:
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)
}
My function to respond to the file selection:
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) {
val filenameURIStr = selectedFilename.toString()
if (filenameURIStr.endsWith(".txt", true)) {
val msg = "Chosen file: " + filenameURIStr
val toast = Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT)
toast.show()
File(selectedFilename.getPath()).forEachLine {
val toast = Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT)
toast.show()
}
}
else {
val msg = "The chosen file is not a .txt file!"
val toast = Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG)
toast.show()
}
}
else {
val msg = "Null filename data received!"
val toast = Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG)
toast.show()
}
}
}
The FileNotFound exception is thrown on the line where it creates the File object to do the forEachLine loop:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=111, result=-1, data=Intent { dat=content://com.android.externalstorage.documents/document/0000-0000:Sudoku puzzles/hard001.txt flg=0x1 }} to activity {com.example.sudokusolver/com.example.sudokusolver.MainActivity}: java.io.FileNotFoundException: /document/0000-0000:Sudoku puzzles/hard001.txt (No such file or directory)
Solution 1:[1]
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."
Solution 2:[2]
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
Solution 3:[3]
Open a Bitmap file given its 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;
}
Solution 4:[4]
For msf:
file uri format, present from Andorid 10.
you can check this solution: https://stackoverflow.com/a/68827976/15342371
This fetch path without copying the file.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | SANAT |
Solution 2 | Andrew |
Solution 3 | miraquee |
Solution 4 | Sanskar Dahiya |