Gaurav Pingale
Gaurav Pingale

Reputation: 454

Flutter : Create directory on external storage path - path provider getExternalStorageDirectory()

I'm creating a flutter app where I want to download and store an image to the external storage (not documents directory) so it can be viewed by any photo gallery app. I'm using the following code to create a directory

var dir = await getExternalStorageDirectory();
  if(!Directory("${dir.path}/myapp").existsSync()){
    Directory("${dir.path}/myapp").createSync(recursive: true);
  }

It's giving me following error:

FileSystemException: Creation failed, path = '/storage/emulated/0/myapp' (OS Error: Permission denied, errno = 13)

I have set up permissions in the manifest file and using the following code for runtime permissions

List<Permissions> permissions = await Permission.getPermissionStatus([PermissionName.Storage]);
permissions.forEach((p) async {
  if(p.permissionStatus != PermissionStatus.allow){
    final res = await Permission.requestSinglePermission(PermissionName.Storage);
    print(res);
  }
});

I have verified in settings that app has got the permission, also as suggested on some answers here I've also tried giving permission from settings app manually which did not work.

Upvotes: 13

Views: 32723

Answers (4)

Vishal_VE
Vishal_VE

Reputation: 2127

For Android 11 and higher, android change his policy so you have add permission at AndroidManifest..

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

And Also add these two line inside <application ......

android:requestLegacyExternalStorage="true"
android:preserveLegacyExternalStorage="true"

Upvotes: 1

St&#233;phane COUGET
St&#233;phane COUGET

Reputation: 221

here is an operational and 100% Dart code to make things easier :

import 'dart:async';
import 'dart:developer';
import 'dart:io';

import 'package:path/path.dart' as Path;
import 'package:path_provider/path_provider.dart';

///getExternalStoragePublicDirectory
enum extPublicDir {
  Music,
  PodCasts,
  Ringtones,
  Alarms,
  Notifications,
  Pictures,
  Movies,
  Download,
  DCIM,
  Documents,
  Screenshots,
  Audiobooks,
}
/// use in loop or without:
/// generation loop of a creation of the same directory in a list
/// public or shared folders by the Android system
/*
 for (var ext in extPublicDir.values) {

   ExtStorage.createFolderInPublicDir(
     type: ext,  //or without loop : extPublicDir.Download,
     folderName: "folderName",  // folder or folder/subFolder/... to create
   );

 }
*/
/// provided the ability to create folders and files within folders
/// public or shared from the Android system
///
/// /storage/emulated/0/Audiobooks
/// /storage/emulated/0/PodCasts
/// /storage/emulated/0/Ringtones
/// /storage/emulated/0/Alarms
/// /storage/emulated/0/Notifications
/// /storage/emulated/0/Pictures
/// /storage/emulated/0/Movies
/// storage/emulated/0/Download
/// /storage/emulated/0/DCIM
/// /storage/emulated/0/Documents
/// /storage/emulated/0/Screenshots  //Screenshots dropping ?
/// /storage/emulated/0/Music/

class ExtStorage {

  //According to path_provider
  static Future<String> get _directoryPathESD async {
    var directory = await getExternalStorageDirectory();
    if (directory != null) {
      log('directory:${directory.path}');

      return directory.path;
    }
    log('_directoryPathESD==null');

    return '';
  }

  /// create or not, but above all returns the created folder in a public folder
  /// official, folderName = '', only return the public folder: useful for
  /// manage a file at its root
  static Future<String> createFolderInPublicDir({
    required extPublicDir type,
    required String folderName,
  }) async {

    var _appDocDir = await _directoryPathESD;

    log("createFolderInPublicDir:_appDocDir:${_appDocDir.toString()}");

    var values = _appDocDir.split("${Platform.pathSeparator}");
    values.forEach(print);

    var dim = values.length - 4; // Android/Data/package.name/files
    _appDocDir = "";

    for (var i = 0; i < dim; i++) {
      _appDocDir += values[i];
      _appDocDir += "${Platform.pathSeparator}";
    }
    _appDocDir += "${type.toString().split('.').last}${Platform.pathSeparator}";
    _appDocDir += folderName;

    log("createFolderInPublicDir:_appDocDir:$_appDocDir");

    if (await Directory(_appDocDir).exists()) {
      log("createFolderInPublicDir:reTaken:$_appDocDir");

      return _appDocDir;
    } else {
      log("createFolderInPublicDir:toCreate:$_appDocDir");
      //if folder not exists create folder and then return its path
      final _appDocDirNewFolder =
          await Directory(_appDocDir).create(recursive: true);
      final pathNorma = Path.normalize(_appDocDirNewFolder.path);
      log("createFolderInPublicDir:ToCreate:pathNorma:$pathNorma");

      return pathNorma;
    }
  }
}

Upvotes: 3

live-love
live-love

Reputation: 52366

You need to request permissions before saving a file using getExternalStorageDirectory.

Add this to Androidmanifest.xml:

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Then use the permission_handler package to get Storage permission:

https://pub.dev/packages/permission_handler

If you are running in the Emulator, getExternalStorageDirectory returns:

/storage/emulated/0/Android/data/com.example.myapp/files/

If you just need the external dir to create a directory under it, then just use:

/storage/emulated/0/

You can create a directory under this folder, so they user will be able to open the files.

Upvotes: 6

Shyju M
Shyju M

Reputation: 9923

The below code is working fine in my application to download an image using the url to the external storage

Future<bool> downloadImage(String url) async {
  await new Future.delayed(new Duration(seconds: 1));
  bool checkResult =
      await SimplePermissions.checkPermission(Permission.WriteExternalStorage);
  if (!checkResult) {
    var status = await SimplePermissions.requestPermission(
        Permission.WriteExternalStorage);
    if (status == PermissionStatus.authorized) {
      var res = await saveImage(url);
      return res != null;
    }
  } else {
    var res = await saveImage(url);
    return res != null;
  }
  return false;
}

Future<Io.File> saveImage(String url) async {
  try {
    final file = await getImageFromNetwork(url);
    var dir = await getExternalStorageDirectory();
    var testdir =
        await new Io.Directory('${dir.path}/iLearn').create(recursive: true);
    IM.Image image = IM.decodeImage(file.readAsBytesSync());
    return new Io.File(
        '${testdir.path}/${DateTime.now().toUtc().toIso8601String()}.png')
      ..writeAsBytesSync(IM.encodePng(image));
  } catch (e) {
    print(e);
    return null;
  }
}

  Future<Io.File> getImageFromNetwork(String url) async {
  var cacheManager = await CacheManager.getInstance();
  Io.File file = await cacheManager.getFile(url);
  return file;
}

Namespaces

import 'dart:io' as Io;
import 'package:image/image.dart' as IM;
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:path_provider/path_provider.dart';
import 'package:simple_permissions/simple_permissions.dart';

Hope it helps

Upvotes: 4

Related Questions