qing
qing

Reputation: 885

How to solve OS Error: Permission denied, errno = 13 in flutter

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

Answers (13)

Hưng Trịnh
Hưng Trịnh

Reputation: 1047

I had the same problem

My solution:

use permission_handler

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

Covenant T. Junior
Covenant T. Junior

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: perm1

perm2

perm3

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

omega_mi
omega_mi

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

Ramkesh Yadav
Ramkesh Yadav

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

MHappersberger
MHappersberger

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

mika
mika

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

Singh Manish
Singh Manish

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

Daniel
Daniel

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

Charith Jayasanka
Charith Jayasanka

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

gokul656
gokul656

Reputation: 313

Just change the targetSdkVersion to 28 from the build.gradle file. Rebuild app and run, it's working fine.

Upvotes: 1

Amal Paul
Amal Paul

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

Rajil TL
Rajil TL

Reputation: 1165

In Android Q, you need to add the Following Lines in AndroidManifest file:

 <application
      android:requestLegacyExternalStorage="true"

Upvotes: 76

Aamil Silawat
Aamil Silawat

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

Related Questions