56

I am generating an excelsheet in my app which when generated should be automatically saved in the "Downloads" folder of any android device where all the downloads are typically saved.

I have the following which saves the file under "My Files" folder -

File file = new File(context.getExternalFilesDir(null), fileName);

resulting in -

W/FileUtils﹕ Writing file/storage/emulated/0/Android/data/com.mobileapp/files/temp.xls

I rather want to save the generated file automatically in the "Downloads" folder when the excel sheet is generated.

Update # 1: Please see the snapshot here. What I want is the one circled in red and what you suggested gets stored in the one circled blue (/storage/emulated/0/download) if that makes sense. Please advise on how I can save a file in the one circled red i.e., "Downloads" folder which is different from /storage/emulated/0/Download under "MyFiles"

snapshot

sunskin
  • 1,620
  • 3
  • 25
  • 49

8 Answers8

70

Use this to get the directory:

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

And don't forget to set this permission in your manifest.xml:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
John Smith
  • 7,243
  • 6
  • 49
  • 61
Oluwatumbi
  • 1,321
  • 1
  • 12
  • 19
  • would that be File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);? – sunskin Jan 28 '15 at 03:15
  • 2
    Just use straith like: File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); – Oluwatumbi Jan 28 '15 at 03:21
  • Yes, I did. It says "W/FileUtils﹕ Writing file/storage/emulated/0/Download/temp.xls" and I don't see that in the "Downloads" folder straight up from the app icon tray. I don't want to locate "Download" folder under "My Files" if that is what you suggested – sunskin Jan 28 '15 at 03:29
  • I want the file to be saved/stored into the "Downloads" folder (the same place the attachments downloaded from your email goes into) – sunskin Jan 28 '15 at 03:32
  • 1
    this is what it returns to me: storage/sdcard0/Downloads which is what you need. Try to probably Toast the file.getAbsolutePath() on the directory before you save your file. And try if you could edit your question to show how you are saving the file – Oluwatumbi Jan 28 '15 at 03:42
  • 50
    This answer is deprecated in API 29 – eri0o Dec 16 '19 at 15:41
  • 5
    is there any alternative for getExternalStoragePublicDirectory ? since it is depricated ? – LMK IND Dec 24 '19 at 03:23
  • I get the following error when I do this on Android 10 (API 29): `android.system.ErrnoException: open failed: EACCES (Permission denied)` – Sam Feb 01 '20 at 07:01
  • 4
    Here's how to do it in API 29: https://stackoverflow.com/a/56990305/238753 – Sam Feb 01 '20 at 07:36
15

Now from your question edit I think understand better what you want. Files shown on the Downloads menu from the one circled in red are ones that are actually downloaded via the DownloadManager, though the previos steps I gave you will save files in your downloads folder but they will not show in this menu because they weren't downloaded. However to make this work, you have to initiate a download of your file so it can show here.

Here is an example of how you can start a download:

 DownloadManager.Request request = new DownloadManager.Request(uri);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "fileName");
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); // to notify when download is complete
request.allowScanningByMediaScanner();// if you want to be available from media players
DownloadManager manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
manager.enqueue(request);

This method is used to download from a Uri and i have not used it with a local file.

If your file is not from the internet you could try saving a temporary copy and get the Uri of the file for this value.

Oluwatumbi
  • 1,321
  • 1
  • 12
  • 19
14

Just use the DownloadManager to download your generated file like this:

File dir = new File("//sdcard//Download//");

File file = new File(dir, fileName);

DownloadManager downloadManager = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);

downloadManager.addCompletedDownload(file.getName(), file.getName(), true, "text/plain",file.getAbsolutePath(),file.length(),true);

The "text/plain" is the mime type you pass so it will know which applications can run the downloadedfile. That did it for me.

Axel
  • 3,331
  • 11
  • 35
  • 58
Thiago Prochnow
  • 139
  • 1
  • 4
10

For API 29 and above

According to documentation, it is required to use Storage Access Framework for Other types of shareable content, including downloaded files.

System file picker should be used to save file to External storage directory.

Copy file to external storage example:

// use system file picker Intent to select destination directory
private fun selectExternalStorageFolder(fileName: String) {
    val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "*/*"
        putExtra(Intent.EXTRA_TITLE, name)
    }
    startActivityForResult(intent, FILE_PICKER_REQUEST)
}

// receive Uri for selected directory
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        FILE_PICKER_REQUEST -> data?.data?.let { destinationUri ->
            copyFileToExternalStorage(destinationUri)
        }
    }
}

// use ContentResolver to write file by Uri
private fun copyFileToExternalStorage(destination: Uri) {
    val yourFile: File = ...

    try {
        val outputStream = contentResolver.openOutputStream(destination) ?: return
        outputStream.write(yourFile.readBytes())
        outputStream.close()
    } catch (e: IOException) {
        e.printStackTrace()
    }
}
Mikhail Sharin
  • 3,661
  • 3
  • 27
  • 36
  • If I try to save TXT file this way - everything is OK. But if I try to save PDF file with type = "application/pdf" and putExtra(Intent.EXTRA_TITLE, "tempFileName.pdf") - then I receive a PDF file, which I can't open anywhere (smartphone, PC) with error: "The document cannot be opened because it is corrupted or damaged". What could be wrong? @Mikhail Sharin – Tim Kruichkov Jan 26 '22 at 18:33
  • To get an instance of contentResolver use: val contentResolver = applicationContext.contentResolver – Jonny Right May 09 '23 at 13:43
8

I used the following code to get this to work in my Xamarin Droid project with C#:

// Suppose this is your local file
var file = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
var fileName = "myFile.pdf";

// Determine where to save your file
var downloadDirectory = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
var filePath = Path.Combine(downloadDirectory, fileName);

// Create and save your file to the Android device
var streamWriter = File.Create(filePath);
streamWriter.Close();
File.WriteAllBytes(filePath, file);

// Notify the user about the completed "download"
var downloadManager = DownloadManager.FromContext(Android.App.Application.Context);
downloadManager.AddCompletedDownload(fileName, "myDescription", true, "application/pdf", filePath, File.ReadAllBytes(filePath).Length, true);

Now your local file is "downloaded" to your Android device, the user gets a notification, and a reference to the file is being added to the downloads folder. Make sure, though, to ask the user for permission before you write to the file system, otherwise an 'access denied' exception will be thrown.

Sjoerdson
  • 123
  • 2
  • 9
2

As Oluwatumbi have answered this question correctly but First you need to check for permission is granted for WRITE_EXTERNAL_STORAGE by following code:

int MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;

if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission is not granted
        // Request for permission
        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);

    }else{
        Uri uri = Uri.parse(yourUrl);
        DownloadManager.Request request = new DownloadManager.Request(uri);
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "fileName");
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); // to notify when download is complete
        request.allowScanningByMediaScanner();// if you want to be available from media players
        DownloadManager manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        manager.enqueue(request);
    }
M.Usman
  • 2,049
  • 22
  • 25
0

This is running code download files using DownloadManager for Xamarin android api version 29

public bool DownloadFileByDownloadManager(string imageURL)
        {
            try
            {
                string file_ext = Path.GetExtension(imageURL);
                string file_name = "MyschoolAttachment_" + DateTime.Now.ToString("yyyy MM dd hh mm ss").Replace(" ", "") + file_ext;
                var path = global::Android.OS.Environment.DirectoryDownloads;

                Android.Net.Uri uri = Android.Net.Uri.Parse(imageURL);
                DownloadManager.Request request = new DownloadManager.Request(uri);
                request.SetDestinationInExternalPublicDir(path, file_name);
                request.SetTitle(file_name);
                request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted); // to notify when download is complete

                // Notify the user about the completed "download"
                var downloadManager = DownloadManager.FromContext(Android.App.Application.Context);

                var d_id = downloadManager.Enqueue(request);
                return true;
            }
            catch (Exception ex)
            {
                UserDialogs.Instance.Alert("Error in download attachment.", "Error", "OK");
                System.Diagnostics.Debug.WriteLine("Error !" + ex.ToString());
                return false;
            }
        }
-1

WORKING ON API 29 AND 30

To get the directory:

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

Set this permission in your AndroidManifest.xml:

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

Add requestLegacyExternalStorage to your application tag in AndroidManifest.xml (for API 29):

<application
    ...
    android:requestLegacyExternalStorage="true">
shamlos
  • 22
  • 1
  • 1
    according to https://developer.android.com/about/versions/11/privacy/storage if you target Android 11 (API 30) the OS ignores the `android:requestLegacyExternalStorage` flag – Adam Burley Jan 31 '22 at 10:02