flutter_downloader
flutter_downloader copied to clipboard
[android] Exception when removing a file from application's files directory
I store the downloaded files in the application's files directory (using getApplicationDocumentsDirectory()
of the path_provider
plugin). It works ok excepted when trying to remove the file on Android with the remove
method, I get the following exception:
E/MethodChannel#vn.hunghd/downloader(25411): Failed to handle method call
E/MethodChannel#vn.hunghd/downloader(25411): java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider
uri content://media/external/images/media from pid=25411, uid=10138 requires android.permission.READ_EXTERNAL_STORAGE,
or grantUriPermission()
E/MethodChannel#vn.hunghd/downloader(25411): at android.os.Parcel.readException(Parcel.java:1954)
E/MethodChannel#vn.hunghd/downloader(25411): at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
E/MethodChannel#vn.hunghd/downloader(25411): at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
E/MethodChannel#vn.hunghd/downloader(25411): at android.content.ContentProviderProxy.query(ContentProviderNative.java:418)
E/MethodChannel#vn.hunghd/downloader(25411): at android.content.ContentResolver.query(ContentResolver.java:766)
E/MethodChannel#vn.hunghd/downloader(25411): at android.content.ContentResolver.query(ContentResolver.java:716)
E/MethodChannel#vn.hunghd/downloader(25411): at android.content.ContentResolver.query(ContentResolver.java:667)
E/MethodChannel#vn.hunghd/downloader(25411): at vn.hunghd.flutterdownloader.FlutterDownloaderPlugin.deleteFileInMediaStore(FlutterDownloaderPlugin.java:362)
E/MethodChannel#vn.hunghd/downloader(25411): at vn.hunghd.flutterdownloader.FlutterDownloaderPlugin.remove(FlutterDownloaderPlugin.java:332)
E/MethodChannel#vn.hunghd/downloader(25411): at vn.hunghd.flutterdownloader.FlutterDownloaderPlugin.onMethodCall(FlutterDownloaderPlugin.java:100)
E/MethodChannel#vn.hunghd/downloader(25411): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
E/MethodChannel#vn.hunghd/downloader(25411): at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
E/MethodChannel#vn.hunghd/downloader(25411): at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:692)
E/MethodChannel#vn.hunghd/downloader(25411): at android.os.MessageQueue.nativePollOnce(Native Method)
E/MethodChannel#vn.hunghd/downloader(25411): at android.os.MessageQueue.next(MessageQueue.java:379)
E/MethodChannel#vn.hunghd/downloader(25411): at android.os.Looper.loop(Looper.java:144)
E/MethodChannel#vn.hunghd/downloader(25411): at android.app.ActivityThread.main(ActivityThread.java:7529)
E/MethodChannel#vn.hunghd/downloader(25411): at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#vn.hunghd/downloader(25411): at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
E/MethodChannel#vn.hunghd/downloader(25411): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
So, the android.permission.READ_EXTERNAL_STORAGE
is required also I don't use it... The problem comes from the FlutterDownloaderPlugin.deleteFileInMediaStore
, on this line:
Cursor imageCursor = contentResolver.query(imageQueryUri, projection, imageSelection, selectionArgs, null);
The method makes the assumption that the file must be either in the "image store" or in the "video store", which causes the error when it's not. This is weird to make this assumption when removing the file while such constraint doesn't exist when creating it. I am not an Android developer so maybe there's a reason that I don't grasp.
However, I have made the following edits and it works now (the files are deleted with no errors):
- include the code in a try/catch block to avoid the exception
- As a last resort attempt to delete the file using
File.delete()
:
try {
// search the file in image store first
Cursor imageCursor = contentResolver.query(imageQueryUri, projection, imageSelection, selectionArgs, null);
if (imageCursor != null && imageCursor.moveToFirst()) {
// We found the ID. Deleting the item via the content provider will also remove the file
long id = imageCursor.getLong(imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
Uri deleteUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
contentResolver.delete(deleteUri, null, null);
} else {
// File not found in image store DB, try to search in video store
Cursor videoCursor = contentResolver.query(imageQueryUri, projection, imageSelection, selectionArgs, null);
if (videoCursor != null && videoCursor.moveToFirst()) {
// We found the ID. Deleting the item via the content provider will also remove the file
long id = videoCursor.getLong(videoCursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
Uri deleteUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
contentResolver.delete(deleteUri, null, null);
} else {
// can not find the file in media store DB at all -> do a simple delete
file.delete(); // <=================== FILES GET SUCCESSFULLY DELETED HERE
}
if (videoCursor != null) videoCursor.close();
}
if (imageCursor != null) imageCursor.close();
} catch (Exception e) {
// fail silently
}
I can submit a PR if there's a chance it gets merged soon.
I don't understand the why of this ContentResolver
approach when a simple File.delete()
does seem to do the job, but as I said I am not an Android developer. I'd be glad if someone could explain.
Note: I think the deletion from the video store has also a problem, as it uses the same settings as for the image store. On this line:
Cursor videoCursor = contentResolver.query(imageQueryUri, projection, imageSelection, selectionArgs, null);
shouldn't videoQueryUri
and videoSelection
be used instead of imageQueryUri
and imageSelection
?
I've found this too, In our application we're aiming to save these files to the app storage however it attempts to remove downloaded files from external storage. Doesn't seem like you can really specify where to remove from - this PR would really help!
Just note that this error will still be thrown and you probably need to check to see if the device has permissions to access the external storage first before trying to query if the items exists in external storage.
Just note that this error will still be thrown and you probably need to check to see if the device has permissions to access the external storage first before trying to query if the items exists in external storage.
This is not what I have experienced: the exception was gone either by granting this permission to the app, or by adding the try/catch. Why do you think the error will still be thrown despite the try/catch?
This commit introduced the code about removing the file from "media store DB" https://github.com/fluttercommunity/flutter_downloader/commit/49332f612ac0c7ee16aa0e50e8ceae1c0941ed2c
@hnvn any idea why this was necessary?
I have prepared fix here for this issue. https://github.com/alhafoudh/flutter_downloader/tree/fix-permission-denied Please test it out. I can submit this as PR afterwards.
any update on the latest release?
any news here?
any news here?
I fixed in in #871 :)