I'm trying to check if a file exists prior to creating it, using DocumentFile (due to the Storage Access Framework). But DocumentFile().fromTreeUri()
removes the non-existant portion of the would-be Uri, which then causes DocumentFile().exists()
to always return true, regardless of whether it exists or not.
I've created a simple example to demonstrate my point. First we ask the user to select a directory:
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Ask the user for the source folder
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, 100);
}
On response, we add /fictionalFile
to the path (thus making it a non-existent file), and then check if it exists:
public void onActivityResult(int requestCode, int resultCode, Intent resultData)
{
if (resultCode == RESULT_OK)
{
if(requestCode == 100)
{
Uri fictionalURI = Uri.parse(resultData.getData()+"/fictionalFile");
DocumentFile fictionalFile = DocumentFile.fromTreeUri(this, fictionalURI);
Log.i("STORAGE", "FICTIONAL URI: "+fictionalURI);
Log.i("STORAGE", "FICTIONAL DOCUMENTFILE URI: "+fictionalFile.getUri());
if(fictionalFile.exists())
{
Log.i("STORAGE", "Fictional file exists");
}
}
}
}
However, when DocumentFile.fromTreeUri() is run on the fictional Uri, the fake "/fictionalfile" portion is lost, which then causes the DocumentFile.exists() function to return true, as shown by the below LogCat:
I/STORAGE: FICTIONAL URI: content://com.android.externalstorage.documents/tree/17FA-1C18%3AFileSync%2Ftarget/fictionalFile
I/STORAGE: FICTIONAL DOCUMENTFILE URI: content://com.android.externalstorage.documents/tree/17FA-1C18%3AFileSync%2Ftarget/document/17FA-1C18%3AFileSync%2Ftarget
I/STORAGE: Fictional file exists
(In the above example, I'm using the SD card, thus the long path names)
Is there another way to check if a not-yet created DocumentFile exists? The use case is that when copying a file from Directory A to Directory B, I want to check if said file already exists in Directory B prior to starting the transfer.
UPDATE: I've now realised that using DocumentFile.fromTreeUri()
is wrong, and that I should be using DocumentFile.fromSingleUri()
. This helps, but upon running .exists()
on the new file I am getting W/DocumentFile: Failed query: java.lang.UnsupportedOperationException: Unsupported Uri content://com.android.externalstorage.documents/tree/17FA-1C18%3AFileSync%2Ftarget/fictionalFile
. Any thoughts?
public void onActivityResult(int requestCode, int resultCode, Intent resultData)
{
if (resultCode == RESULT_OK)
{
if(requestCode == 100)
{
Uri fictionalURI = Uri.parse(resultData.getData()+"/fictionalFile");
DocumentFile fictionalFile = DocumentFile.fromSingleUri(this, fictionalURI);
Log.i("STORAGE", "FICTIONAL URI: "+fictionalURI);
Log.i("STORAGE", "FICTIONAL DOCUMENTFILE URI: "+fictionalFile.getUri());
if(fictionalFile != null && fictionalFile.exists())
{
Log.i("STORAGE", "Fictional file exists");
}
}
}
}
Given treeUri
as being the Uri
returned by ACTION_OPEN_DOCUMENT_TREE
, wrap treeUri
in a DocumentFile
using fromTreeUri()
, then call findFile()
on that DocumentFile
supplying the display name that you are looking for (e.g., fictionalFile
). If it returns null
, there is no file matching that display name.
IOW:
if (DocumentFile.fromTreeUri(this, treeUri).findFile(whatevs) == null) {
// TODO: something
}
Note, though, that "display name" is not necessarily a filename.
In my case, when I checked DocumentFile.fromSingleUri(context, media.fileUri)?.exists() == true
in CoroutineWorker with applicationContext, DocumentProvider always returned true, when file was deleted from outside application, for example, in file manager.
The problem was solved with uri.isFileExist(context)
import android.content.Context
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
fun DocumentFile?.isFile() = this?.isFile ?: false
fun DocumentFile?.isExists() = this?.exists() ?: false
fun DocumentFile?.getLength() = this?.length() ?: 0
fun DocumentFile?.isFileExist() = isFile() && isExists() && getLength() > 0
fun Uri.isFileExist(context: Context) =
DocumentFile.fromSingleUri(context, this).isFileExist()
UPDATE: The solution below works correctly on android 10+. I ended up replacing it to get it working properly on all versions of android.
fun Uri.isFileExist(context: Context): Boolean {
var isExist = false
runCatching {
context.contentResolver.openInputStream(this)?.let {
it.close()
isExist = true
}
}
return isExist
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With