fluttercommunity / flutter_downloader

Flutter Downloader - A plugin for creating and managing download tasks.
https://pub.dev/packages/flutter_downloader
BSD 3-Clause "New" or "Revised" License
911 stars 511 forks source link

Path is not taking as given and downloading in Download folder only #533

Open pratik-7span opened 3 years ago

pratik-7span commented 3 years ago

I am getting issues while downloading videos from the server. I had given the path for Android as (/storage/emulated/0/Movies/MyApp) but It's downloading video in the "Download" folder only.

Here is my code:

requestDownloadFile(String link, bool isShare) async {
    _permissionReady = await _checkPermission();

    if (_permissionReady) {
      FlutterDownloader.registerCallback(callback);

      var _directory = Platform.isAndroid ? Directory('/storage/emulated/0/Movies/MyApp') : await getApplicationDocumentsDirectory();

      String newPath = _directory.absolute.path;

      print("Absolute Path: $newPath");

      if(Platform.isIOS) {
        newPath += '/MyApp';
      }
      _directory = Directory(newPath);

      bool hasExisted = await _directory.exists();
      if (!hasExisted) {
        _directory.create();
      }

      _localPath = _directory.path;

      print(_localPath);

      String fileName = link.split('/').last;

      print(fileName);

      print(_localPath + '/' + fileName);

      String localFilePath = _localPath + '/' + fileName;

      if (await File(localFilePath).exists() && isShare) {
        Share.shareFiles([localFilePath]);
      } else {
        ReceivePort _port = ReceivePort();
        IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');

        streamSubscription = _port.listen((dynamic data) {
          DownloadTaskStatus status = data[1];
          if (status.value == 3) {
            print(fileName);
            print('Downloaded Path: ${_localPath + ' / ' + fileName}');
            streamSubscription.cancel();
            IsolateNameServer.removePortNameMapping('downloader_send_port');

            // Share only if clicked on share otherwise download
            if (isShare) Share.shareFiles([_localPath + '/' + fileName]);
          }
        });

        print('Path Before Downloading: $_localPath');
        await FlutterDownloader.enqueue(url: link, savedDir: _localPath, showNotification: true, openFileFromNotification: true, fileName: fileName);
      }
    }
  }

Here are the logs:

I/flutter (25574): Absolute Path: /storage/emulated/0/Movies/MyApp
I/flutter (25574): Directory: '/storage/emulated/0/Movies/MyApp'
I/flutter (25574): /storage/emulated/0/Movies/MyApp
I/flutter (25574): gbUG2Ag6LHA4UGoyG2agOeV6Uu03Thn8RVcsTu1Q.mp4
I/flutter (25574): /storage/emulated/0/Movies/MyApp/gbUG2Ag6LHA4UGoyG2agOeV6Uu03Thn8RVcsTu1Q.mp4
I/flutter (25574): Path Before Downloading: /storage/emulated/0/Movies/MyApp

D/DownloadWorker(25574): DownloadWorker{url=https://example.com/gbUG2Ag6LHA4UGoyG2agOeV6Uu03Thn8RVcsTu1Q.mp4,filename=gbUG2Ag6LHA4UGoyG2agOeV6Uu03Thn8RVcsTu1Q.mp4,savedDir=/storage/emulated/0/Movies/MyApp,header=,isResume=false

But after downloading its printing (from the library) as below:

D/DownloadWorker(25574): File downloaded (/storage/emulated/0/Download/gbUG2Ag6LHA4UGoyG2agOeV6Uu03Thn8RVcsTu1Q.mp4)
D/DownloadWorker(25574): Setting an intent to open the file /storage/emulated/0/Download/gbUG2Ag6LHA4UGoyG2agOeV6Uu03Thn8RVcsTu1Q.mp4

Can you please tell me why it's downloading the video in the Download folder only? Why it's not taking the path I have given?

abeyshiferaw0 commented 3 years ago

i am also facing the same issue, i wanted to download a file to private storage by path provided by getApplicationSupportDirectory but the package keeps downloading to downloads folder in the external storage

DpkPatil05 commented 3 years ago

Same issue here

abeyshiferaw0 commented 3 years ago

i have checked the native android implementation, this isn't a bug don't know why but the path provided is ignored and is replaced by downloads media folder intentionally,

@RequiresApi(Build.VERSION_CODES.Q)
private Uri addFileToDownloadsApi29(String filename) {
    Uri collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
    try {
        ContentValues values = new ContentValues();
        values.put(MediaStore.Downloads.DISPLAY_NAME, filename);
        ContentResolver contentResolver = getApplicationContext().getContentResolver();
        return contentResolver.insert(collection, values);
    } catch (Exception e) {
        e.printStackTrace();
        log("Create a file using MediaStore API failed ");
    }
    return null;
}

its in DownloadWorker.java

abeyshiferaw0 commented 3 years ago

For any one who wants to the downloaded files to private or local storage

1.go to the android implementation and look for DownloadWorkder.java file

2.look for this function addFileToDownloadsApi21 and replace its content with this File newFile = new File(getApplicationContext().getFilesDir(), filename); return newFile;

  1. also comment this // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // uriApi29 = addFileToDownloadsApi29(filename); // if (isResume) { // outputStream = context.getContentResolver().openOutputStream(uriApi29, "wa"); // } else { // outputStream = context.getContentResolver().openOutputStream(uriApi29, "w"); // } // } else { // fileApi21 = addFileToDownloadsApi21(filename); // outputStream = new FileOutputStream(fileApi21, isResume); // }

and add this to bottom

fileApi21 = addFileToDownloadsApi21(filename); outputStream = new FileOutputStream(fileApi21, isResume);

Also comment out a big chunk of code in => if (status == DownloadStatus.COMPLETE) {}. condition

seems reckless but its a fix, you can file your files in the local storage

ReaganRealones commented 3 years ago

Thank you @abeyshiferaw0 , works perfectly. Though I was wondering when this can be fixed in the master repo

AbdulrhmanBazrto commented 3 years ago

having same issue here, how you solve it? @abeyshiferaw0 please can you explain how to go to "go to the android implementation" ? I'm still having same problem I can't store the files into subfolder of downlad directory

yelkamel commented 3 years ago

Same issue here, can someone do a fork with the fix ? ^^'

JeremyGashu commented 3 years ago

Also comment out a big chunk of code in => if (status == DownloadStatus.COMPLETE) {}. condition

Does it really work for you?

abeyshiferaw0 commented 3 years ago

Also comment out a big chunk of code in => if (status == DownloadStatus.COMPLETE) {}. condition

Does it really work for you?

yes it does work, if you only want the downloaded file to be saved on the local private storage only

abeyshiferaw0 commented 3 years ago

having same issue here, how you solve it? @abeyshiferaw0 please can you explain how to go to "go to the android implementation" ? I'm still having same problem I can't store the files into subfolder of download directory

if your using android studio double click shift and search for downloadWorker.java, open the file and you can edit the changes

abeyshiferaw0 commented 3 years ago

Same issue here, can someone do a fork with the fix ? ^^'

https://github.com/abeyshiferaw0/flutter_downloader.git

this is the fork that i used, but it is only for downloading to internal storage only

just pass your desired location starting from getApplicationSupportDirectory() and it will work on both android and ios

e.g ` /// to get downloaded file save path Future getSaveDir() async { Directory directory = await getApplicationSupportDirectory(); Directory saveDir = Directory("${directory.absolute.path}${Platform.pathSeparator}SUB_FOLDER"); bool exists = await saveDir.exists(); if (!exists) { await saveDir.create(recursive: true); } return saveDir.path; }

/// start download with path from getSaveDir() FlutterDownloader.enqueue( url: URL, savedDir: getSaveDir(), fileName: FILE_NAME, showNotification: true, openFileFromNotification: false, );

`

Cyp commented 3 years ago

This may be duplicate of #525.

franklink commented 3 years ago

This is an epic fail. I have code in the field that is now downloading to the wrong directory. What in the world would make you change this behavior!!!???

hnvn commented 3 years ago

I know this issue, it comes from updates in Android implementation due to support Scoped Storage model in Android 11. In Android 11, app can no longer create dedicated and app-specific directories with external storage. It causes savedDir broken and confused. I am in process to re-design this plugin with new strategy to manage file download location. It is still in triage and discussion so it's very appreciated to have contribution and feedback from developers for this PR

P/S: for temporary solution, I publish v1.7.1 to fix the issue in case of internal storage. Use-case of external storage in Android 11, savedDir will be ignored and downloaded files are always saved to Downloads folder (config by setting saveToPublicStorage to true)

swarupbc commented 2 years ago

@hnvn you can ask for this permission to create the folder await requestPermission(Permission.manageExternalStorage);

Amanullahgit commented 2 years ago

addFileToDownloadsApi21

where can i find this file ?

jr24jr commented 2 years ago

Had the same problem and fixed it.

flutter_downloader: 1.7.0
permission_handler: 8.1.5
external_path: 1.0.1

and replace it with:

flutter_downloader: 1.5.2
permission_handler: 8.1.5
external_path: 1.0.1

to be able to write you must have permissions: storage, manageExternalStorage, requestInstallPackages. in AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<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" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
digambernegi commented 1 year ago

I guess removing or changing "SaveInPublicStorage" to false would save the file to your required directory.

final id = await FlutterDownloader.enqueue( url: url, savedDir: externalDir.path, showNotification: true, openFileFromNotification: true, saveInPublicStorage: false);

Ali-Kheiri commented 1 year ago

@abeyshiferaw0 Is changing download worker java file currently working ? (api v 33)