Reputation: 885
I want to store my document file into '/storage/emulated/0/Download/'. I got this error :
Unhandled Exception: FileSystemException: Cannot open file, path = '/storage/emulated/0/Download/file.pdf' (OS Error: Permission denied, errno = 13)
Here is my code:
void download() async {
http.Response response = await http.post(url, headers: {"Accept": "application/json", HttpHeaders.authorizationHeader: 'Bearer'}});
File file = new File('/storage/emulated/0/Download/file.pdf');
await file.writeAsBytes(response.bodyBytes);
}
Upvotes: 45
Views: 86928
Reputation: 1047
I had the same problem
My solution:
import 'package:permission_handler/permission_handler.dart';
before download action you need check storage permission:
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
Upvotes: 28
Reputation: 66
_AndroidManifest.xml_
file should have these permissions:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:label="AppName"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
Flutter code for requesting permission:
Future<void> requestPermissions() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.manageExternalStorage,
Permission.storage,
].request();
if (statuses.containsValue(PermissionStatus.denied)) {
Fluttertoast.showToast(
msg: "App may malfunction without granted permissions",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 1);
}
}
You should end up with similar interfaces like this:
Now for the file writing and folder creation:
final directory = await getDownloadsDirectory();
final storage = getDeviceInternalPath(directory);
final dir = Directory('/$storage/Download);
if (!await dir.exists()) {
await dir.create(recursive: true);
}
var filePath = '${dir.path}/$_fileName';
final file = File(filePath);
await file.writeAsBytes(response.bodyBytes);
Make use of await getExternalStorageDirectory();
(for sdcard if available) or await getDownloadsDirectory();
(a full permission scope withing the internal device) when saving files, writing to, and creating directories on the device. I made use of await getApplicationDocumentsDirectory();
earlier which resulted in the above error.
They all work fine but the app will only have different file/folder access levels on the device:
await getApplicationDocumentsDirectory();
will provide a scope starting with /data/user/0/
or in full, something like, /data/user/0/com.author.app/app_flutter
which is file-writeable within that scope and for private data too which can be accessible in **Device Storage**/Android/data/(app_package_name)
await getDownloadsDirectory();
or await getExternalStorageDirectory();
on the other hand will provide what you need. And the directory will look like, /storage/emulated/0/
for internal storage and /storage/sdcard/
for obviously, sdcard.
The paths are usually longer than these but I only revealed the prefixes. In my case, I wanted to create a folder in the device's internal storage so I did this to get the first 3 paths:
String getDeviceInternalPath(dir) {
List<String> comp = dir.path.split('/');
List<String> trunc = comp.sublist(1, 4);
return trunc.join('/');
}
Since the first string character is a '/'.
Note, the function above will only work for await getExternalStorageDirectory();
(for sdcard if available) and for await getDownloadsDirectory();
(a full permission scope withing the internal device) when writing to and creating directories on the device.
As for await getApplicationDocumentsDirectory();
we can't do anything about that.
Upvotes: 0
Reputation: 718
For android 11 (api 30) and later, android require MANAGE_EXTERNAL_STORAGE
to read and write file.
Add to your manifest:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
Don't forget to ask permission from flutter using permission_handler
if (Platform.isAndroid && AndroidDeviceInfo().version.sdkInt > 29) {
await Permission.manageExternalStorage.request();
} else {
await Permission.storage.request();
}
Upvotes: 6
Reputation: 1087
After long Research i find this Solution and it will work on all Android 10+
Just change await Permission.storage.request() to await Permission.manageExternalStorage.request() in your Permission Handler or Requester class.
Because this change will give you full access to Media Storage excluding Restricted Storage.
Detail Example:
First Create a method.
Future<AppPermissionStatus> askForPhotoGalleryPermission(
BuildContext context) async {
var status = Platform.isIOS
? await Permission.photos.request()
: await Permission.manageExternalStorage.request();
AppPermissionStatus permissionStatus = AppPermissionStatus.NotAvailable;
if (status.isGranted) {
permissionStatus = AppPermissionStatus.Granted;
return permissionStatus;
} else if (status.isDenied) {
permissionStatus = AppPermissionStatus.Denied;
return permissionStatus;
} else if (status.isRestricted) {
permissionStatus = AppPermissionStatus.Restricted;
return permissionStatus;
} else if (status.isPermanentlyDenied) {
permissionStatus = AppPermissionStatus.PermanentlyDenied;
}
return permissionStatus;
}
How to call this Method.
void getRequiredPermission(BuildContext context) async {
AppPermissionStatus permissionStatus =
await AppPermission().askForPhotoGalleryPermission(context);
setState(() {
if (permissionStatus == AppPermissionStatus.Granted) {
hasPermission = true;
} else if (permissionStatus == AppPermissionStatus.PermanentlyDenied) {
hasPermission = false;
} else {
hasPermission = false;
}
});
}
Upvotes: 0
Reputation: 91
I faced this issue with a Galaxy S9. I couldn't use the last used pictures - I had to click on the gallery and photos tab in the top, the app asked for permission again, and then it worked.
Upvotes: 0
Reputation: 31
My case was that it was possible to access directory. but couldn't read file.
final path= "${await ExternalPath.getExternalStoragePublicDirectory(ExternalPath.DIRECTORY_DOCUMENTS)}/sub";
final dir= Directory(path);
final l = dir.listSync(); // 1. works fine until here
for( final d in l){
try{
final str = await File("${d.path}/info.json").readAsString(); // 2. permission error here
}catch(e){
print(e);
}
}
for solving this problem, I included android.permission.MANAGE_EXTERNAL_STORAGE permission.
like belows.
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:requestLegacyExternalStorage="true"
Upvotes: 2
Reputation: 39
You have to add android:requestLegacyExternalStorage="true"
this in AndroidManifest.xml file.
<application
android:label="app_name"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true"
</application>
Upvotes: 2
Reputation: 1079
In my cases none of the solutions worked, finally what worked was to remove from android/app/build.gradle the reference to "targetSdkVersion XX" inside defaultConfig{}
Now everything works as expected, I guess it is a bug in the framework.
Upvotes: 4
Reputation: 4792
Just add the following lines in to your android manifest if you're on android 10/Q
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:requestLegacyExternalStorage="true"
>
Upvotes: 32
Reputation: 313
Just change the targetSdkVersion to 28
from the build.gradle file. Rebuild app and run, it's working fine.
Upvotes: 1
Reputation: 221
In addition to Rajil TL's answer also include
targetSdkVersion 28
and stop process and run the app. Hot reload is not enough to reflect build.gradle changes.
Upvotes: 6
Reputation: 1165
In Android Q, you need to add the Following Lines in AndroidManifest file:
<application
android:requestLegacyExternalStorage="true"
Upvotes: 76
Reputation: 8229
Make sure you defined permissions in AndroidManifest file
like this :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxx.yyy">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
...
</manifest>
for more info try out this link
Upvotes: 19