Download a File Programmatically on Android

Download a file programmatically on Android

This is some working code I have for downloading a given URL to a given File object. The File object (outputFile) has just been created using new File(path), I haven't called createNewFile or anything.

private static void downloadFile(String url, File outputFile) {
try {
URL u = new URL(url);
URLConnection conn = u.openConnection();
int contentLength = conn.getContentLength();

DataInputStream stream = new DataInputStream(u.openStream());

byte[] buffer = new byte[contentLength];
stream.readFully(buffer);
stream.close();

DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile));
fos.write(buffer);
fos.flush();
fos.close();
} catch(FileNotFoundException e) {
return; // swallow a 404
} catch (IOException e) {
return; // swallow a 404
}
}

Android - How to download a file from a webserver

Using Async task

call when you want to download file : new DownloadFileFromURL().execute(file_url);

public class MainActivity extends Activity {

// Progress Dialog
private ProgressDialog pDialog;
public static final int progress_bar_type = 0;

// File url to download
private static String file_url = "http://www.qwikisoft.com/demo/ashade/20001.kml";

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

new DownloadFileFromURL().execute(file_url);

}

/**
* Showing Dialog
* */

@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case progress_bar_type: // we set this to 0
pDialog = new ProgressDialog(this);
pDialog.setMessage("Downloading file. Please wait...");
pDialog.setIndeterminate(false);
pDialog.setMax(100);
pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pDialog.setCancelable(true);
pDialog.show();
return pDialog;
default:
return null;
}
}

/**
* Background Async Task to download file
* */
class DownloadFileFromURL extends AsyncTask<String, String, String> {

/**
* Before starting background thread Show Progress Bar Dialog
* */
@Override
protected void onPreExecute() {
super.onPreExecute();
showDialog(progress_bar_type);
}

/**
* Downloading file in background thread
* */
@Override
protected String doInBackground(String... f_url) {
int count;
try {
URL url = new URL(f_url[0]);
URLConnection connection = url.openConnection();
connection.connect();

// this will be useful so that you can show a tipical 0-100%
// progress bar
int lenghtOfFile = connection.getContentLength();

// download the file
InputStream input = new BufferedInputStream(url.openStream(),
8192);

// Output stream
OutputStream output = new FileOutputStream(Environment
.getExternalStorageDirectory().toString()
+ "/2011.kml");

byte data[] = new byte[1024];

long total = 0;

while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
// After this onProgressUpdate will be called
publishProgress("" + (int) ((total * 100) / lenghtOfFile));

// writing data to file
output.write(data, 0, count);
}

// flushing output
output.flush();

// closing streams
output.close();
input.close();

} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}

return null;
}

/**
* Updating progress bar
* */
protected void onProgressUpdate(String... progress) {
// setting progress percentage
pDialog.setProgress(Integer.parseInt(progress[0]));
}

/**
* After completing background task Dismiss the progress dialog
* **/
@Override
protected void onPostExecute(String file_url) {
// dismiss the dialog after the file was downloaded
dismissDialog(progress_bar_type);

}

}
}

if not working in 4.0 then add:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

How to download files from net programmatically in Android?

Here's some code that I recently wrote just for that:

    try {
URL u = new URL("http://your.url/file.zip");
InputStream is = u.openStream();

DataInputStream dis = new DataInputStream(is);

byte[] buffer = new byte[1024];
int length;

FileOutputStream fos = new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/" + "file.zip"));
while ((length = dis.read(buffer))>0) {
fos.write(buffer, 0, length);
}

} catch (MalformedURLException mue) {
Log.e("SYNC getUpdate", "malformed url error", mue);
} catch (IOException ioe) {
Log.e("SYNC getUpdate", "io error", ioe);
} catch (SecurityException se) {
Log.e("SYNC getUpdate", "security error", se);
}

This downloads the file and puts it on your sdcard.

You could probably modify this to suit your needs. :)

How to directly download a file to Download directory on Android Q (Android 10)

More than 10 months have passed and yet not a satisfying answer for me have been made. So I'll answer my own question.

As @CommonsWare states in a comment, "get MediaStore.Downloads.INTERNAL_CONTENT_URI or MediaStore.Downloads.EXTERNAL_CONTENT_URI and save a file by using Context.getContentResolver.insert()" is supposed to be the solution. I double checked and found out this is true and I was wrong saying it doesn't work. But...

I found it tricky to use ContentResolver and I was unable to make it work properly. I'll make a separate question with it but I kept investigating and found a somehow satisfying solution.

MY SOLUTION:

Basically you have to download to any directory owned by your app and then copy to Downloads folder.

  1. Configure your app:

    • Add provider_paths.xml to xml resource folder

      <paths xmlns:android="http://schemas.android.com/apk/res/android">
      <external-path name="external_files" path="."/>
      </paths>
    • In your manifest add a FileProvider:

      <application>
      <provider
      android:name="androidx.core.content.FileProvider"
      android:authorities="${applicationId}.provider"
      android:exported="false"
      android:grantUriPermissions="true">
      <meta-data
      android:name="android.support.FILE_PROVIDER_PATHS"
      android:resource="@xml/provider_paths" />
      </provider>
      </application>
  2. Prepare to download files to any directory your app owns, such as getFilesDir(), getExternalFilesDir(), getCacheDir() or getExternalCacheDir().

    val privateDir = context.getFilesDir()
  3. Download file taking its progress into account (DIY):

    val downloadedFile = myFancyMethodToDownloadToAnyDir(url, privateDir, fileName)
  4. Once downloaded you can make any threatment to the file if you'd like to.

  5. Copy it to Downloads folder:

    //This will be used only on android P-
    private val DOWNLOAD_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)

    val finalUri : Uri? = copyFileToDownloads(context, downloadedFile)

    fun copyFileToDownloads(context: Context, downloadedFile: File): Uri? {
    val resolver = context.contentResolver
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    val contentValues = ContentValues().apply {
    put(MediaStore.MediaColumns.DISPLAY_NAME, getName(downloadedFile))
    put(MediaStore.MediaColumns.MIME_TYPE, getMimeType(downloadedFile))
    put(MediaStore.MediaColumns.SIZE, getFileSize(downloadedFile))
    }
    resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
    } else {
    val authority = "${context.packageName}.provider"
    val destinyFile = File(DOWNLOAD_DIR, getName(downloadedFile))
    FileProvider.getUriForFile(context, authority, destinyFile)
    }?.also { downloadedUri ->
    resolver.openOutputStream(downloadedUri).use { outputStream ->
    val brr = ByteArray(1024)
    var len: Int
    val bufferedInputStream = BufferedInputStream(FileInputStream(downloadedFile.absoluteFile))
    while ((bufferedInputStream.read(brr, 0, brr.size).also { len = it }) != -1) {
    outputStream?.write(brr, 0, len)
    }
    outputStream?.flush()
    bufferedInputStream.close()
    }
    }
    }
  6. Once in download folder you can open file from app like this:

    val authority = "${context.packageName}.provider"
    val intent = Intent(Intent.ACTION_VIEW).apply {
    setDataAndType(finalUri, getMimeTypeForUri(finalUri))
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
    addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
    } else {
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
    }
    try {
    context.startActivity(Intent.createChooser(intent, chooseAppToOpenWith))
    } catch (e: Exception) {
    Toast.makeText(context, "Error opening file", Toast.LENGTH_LONG).show()
    }

    //Kitkat or above
    fun getMimeTypeForUri(context: Context, finalUri: Uri) : String =
    DocumentFile.fromSingleUri(context, finalUri)?.type ?: "application/octet-stream"

    //Just in case this is for Android 4.3 or below
    fun getMimeTypeForFile(finalFile: File) : String =
    DocumentFile.fromFile(it)?.type ?: "application/octet-stream"

Pros:

  • Downloaded files survives to app uninstallation

  • Also allows you to know its progress while downloading

  • You still can open them from your app once moved, as the file still belongs to your app.

  • write_external_storage permission is not required for Android Q+, just for this purpose:

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

Cons:

  • You won't have access to downloaded files once after clearing your app data or uninstalling and reinstalling again (they no longer belongs to your app unless you ask for permission)
  • Device must have more free space to be able to copy every file from its original directory to its final destination. This is important speacially for large files. Although if you have access to the original inputStream you could directly write to downloadedUri instead of copying from an intermediary file.

If this approach is enough for you then give it a try.

Android 11 Download File to Download Folder doesn't work

I found an solution on Stackoverflow (Can't find the link anymore)

 private boolean downloadTask(String url) throws Exception {
if (!url.startsWith("http")) {
return false;
}
String name = "temp.mcaddon";
try {
File file = new File(Environment.getExternalStorageDirectory(), "Download");
if (!file.exists()) {
//noinspection ResultOfMethodCallIgnored
file.mkdirs();
}
File result = new File(file.getAbsolutePath() + File.separator + name);
DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
request.setDestinationUri(Uri.fromFile(result));
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
if (downloadManager != null) {
downloadManager.enqueue(request);
}
//mToast(mContext, "Starting download...");
MediaScannerConnection.scanFile(DetailsActivity.this, new String[]{result.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
}
});
} catch (Exception e) {
Log.e(">>>>>", e.toString());
//mToast(this, e.toString());
return false;
}
return true;
}

This should work for Android 11

How to download a file from a server and save it in specific folder in SD card in Android?

Your download URL is not a link to any file. It's a directory. Make sure its a file and exists. Also check your logcat window for error logs. One more suggestion, its always better to do a printStackTrace() in catch blocks instead of Logs. Its gives a more detailed view of the error.

Change this line:

    URL url = new URL("http://myexample.com/android/");

to:

    URL url = new URL("http://myexample.com/android/yourfilename.txt"); //some file url

Next, in catch block, add this line:

e.printStackTrace();

Also in the directory path, it should be something like this:

File dir = new File(root.getAbsolutePath() + "/mnt/sdcard/myclock/databases");

instead of

File dir = new File(root.getAbsolutePath() + "/myclock/databases");

Next, make sure you have acquired permission for writing to external storage in Android manifest.

Is it possible to programmatically access Download folder on Android Q (SDK = 29)?

Is it possible to list the .txt downloaded files programmatically in Android Q (SDK >= 29)?

Not ones created by other apps, which would appear to be your intention.

The closest is if you use ACTION_OPEN_DOCUMENT_TREE and ask the user to open the Downloads/ tree on external storage... and you cannot do that on Android R (at least through DP1).

Download a file with Android, and showing the progress in a ProgressDialog

There are many ways to download files. Following I will post most common ways; it is up to you to decide which method is better for your app.

  1. Use AsyncTask and show the download progress in a dialog
    =============================================================

This method will allow you to execute some background processes and update the UI at the same time (in this case, we'll update a progress bar).

Imports:

import android.os.PowerManager;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;

This is an example code:

// declare the dialog as a member field of your activity
ProgressDialog mProgressDialog;

// instantiate it within the onCreate method
mProgressDialog = new ProgressDialog(YourActivity.this);
mProgressDialog.setMessage("A message");
mProgressDialog.setIndeterminate(true);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(true);

// execute this when the downloader must be fired
final DownloadTask downloadTask = new DownloadTask(YourActivity.this);
downloadTask.execute("the url to the file you want to download");

mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {

@Override
public void onCancel(DialogInterface dialog) {
downloadTask.cancel(true); //cancel the task
}
});

The AsyncTask will look like this:

// usually, subclasses of AsyncTask are declared inside the activity class.
// that way, you can easily modify the UI thread from here
private class DownloadTask extends AsyncTask<String, Integer, String> {

private Context context;
private PowerManager.WakeLock mWakeLock;

public DownloadTask(Context context) {
this.context = context;
}

@Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();

// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}

// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();

// download the file
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/file_name.extension");

byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
if (fileLength > 0) // only if total length is known
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}

if (connection != null)
connection.disconnect();
}
return null;
}

The method above (doInBackground) runs always on a background thread. You shouldn't do any UI tasks there. On the other hand, the onProgressUpdate and onPreExecute run on the UI thread, so there you can change the progress bar:

    @Override
protected void onPreExecute() {
super.onPreExecute();
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
mWakeLock.acquire();
mProgressDialog.show();
}

@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// if we get here, length is known, now set indeterminate to false
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(100);
mProgressDialog.setProgress(progress[0]);
}

@Override
protected void onPostExecute(String result) {
mWakeLock.release();
mProgressDialog.dismiss();
if (result != null)
Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
else
Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
}
}

For this to run, you need the WAKE_LOCK permission.

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

  1. Download from Service
    ========================

The big question here is: how do I update my activity from a service?. In the next example we are going to use two classes you may not be aware of: ResultReceiver and IntentService. ResultReceiver is the one that will allow us to update our thread from a service; IntentService is a subclass of Service which spawns a thread to do background work from there (you should know that a Service runs actually in the same thread of your app; when you extends Service, you must manually spawn new threads to run CPU blocking operations).

Download service can look like this:

public class DownloadService extends IntentService {
public static final int UPDATE_PROGRESS = 8344;

public DownloadService() {
super("DownloadService");
}
@Override
protected void onHandleIntent(Intent intent) {

String urlToDownload = intent.getStringExtra("url");
ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver");
try {

//create url and connect
URL url = new URL(urlToDownload);
URLConnection connection = url.openConnection();
connection.connect();

// this will be useful so that you can show a typical 0-100% progress bar
int fileLength = connection.getContentLength();

// download the file
InputStream input = new BufferedInputStream(connection.getInputStream());

String path = "/sdcard/BarcodeScanner-debug.apk" ;
OutputStream output = new FileOutputStream(path);

byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;

// publishing the progress....
Bundle resultData = new Bundle();
resultData.putInt("progress" ,(int) (total * 100 / fileLength));
receiver.send(UPDATE_PROGRESS, resultData);
output.write(data, 0, count);
}

// close streams
output.flush();
output.close();
input.close();

} catch (IOException e) {
e.printStackTrace();
}

Bundle resultData = new Bundle();
resultData.putInt("progress" ,100);

receiver.send(UPDATE_PROGRESS, resultData);
}
}

Add the service to your manifest:

<service android:name=".DownloadService"/>

And the activity will look like this:

// initialize the progress dialog like in the first example

// this is how you fire the downloader
mProgressDialog.show();
Intent intent = new Intent(this, DownloadService.class);
intent.putExtra("url", "url of the file to download");
intent.putExtra("receiver", new DownloadReceiver(new Handler()));
startService(intent);

Here is were ResultReceiver comes to play:

private class DownloadReceiver extends ResultReceiver{

public DownloadReceiver(Handler handler) {
super(handler);
}

@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {

super.onReceiveResult(resultCode, resultData);

if (resultCode == DownloadService.UPDATE_PROGRESS) {

int progress = resultData.getInt("progress"); //get the progress
dialog.setProgress(progress);

if (progress == 100) {
dialog.dismiss();
}
}
}
}

2.1 Use Groundy library

Groundy is a library that basically helps you run pieces of code in a background service, and it is based on the ResultReceiver concept shown above. This library is deprecated at the moment. This is how the whole code would look like:

The activity where you are showing the dialog...

public class MainActivity extends Activity {

private ProgressDialog mProgressDialog;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String url = ((EditText) findViewById(R.id.edit_url)).getText().toString().trim();
Bundle extras = new Bundler().add(DownloadTask.PARAM_URL, url).build();
Groundy.create(DownloadExample.this, DownloadTask.class)
.receiver(mReceiver)
.params(extras)
.queue();

mProgressDialog = new ProgressDialog(MainActivity.this);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
mProgressDialog.show();
}
});
}

private ResultReceiver mReceiver = new ResultReceiver(new Handler()) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
switch (resultCode) {
case Groundy.STATUS_PROGRESS:
mProgressDialog.setProgress(resultData.getInt(Groundy.KEY_PROGRESS));
break;
case Groundy.STATUS_FINISHED:
Toast.makeText(DownloadExample.this, R.string.file_downloaded, Toast.LENGTH_LONG);
mProgressDialog.dismiss();
break;
case Groundy.STATUS_ERROR:
Toast.makeText(DownloadExample.this, resultData.getString(Groundy.KEY_ERROR), Toast.LENGTH_LONG).show();
mProgressDialog.dismiss();
break;
}
}
};
}

A GroundyTask implementation used by Groundy to download the file and show the progress:

public class DownloadTask extends GroundyTask {    
public static final String PARAM_URL = "com.groundy.sample.param.url";

@Override
protected boolean doInBackground() {
try {
String url = getParameters().getString(PARAM_URL);
File dest = new File(getContext().getFilesDir(), new File(url).getName());
DownloadUtils.downloadFile(getContext(), url, dest, DownloadUtils.getDownloadListenerForTask(this));
return true;
} catch (Exception pokemon) {
return false;
}
}
}

And just add this to the manifest:

<service android:name="com.codeslap.groundy.GroundyService"/>

It couldn't be easier I think. Just grab the latest jar from Github and you are ready to go. Keep in mind that Groundy's main purpose is to make calls to external REST apis in a background service and post results to the UI with easily. If you are doing something like that in your app, it could be really useful.

2.2 Use https://github.com/koush/ion


  1. Use DownloadManager class (GingerBread and newer only)
    =============================================================

GingerBread brought a new feature, DownloadManager, which allows you to download files easily and delegate the hard work of handling threads, streams, etc. to the system.

First, let's see a utility method:

/**
* @param context used to check the device version and DownloadManager information
* @return true if the download manager is available
*/
public static boolean isDownloadManagerAvailable(Context context) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
return true;
}
return false;
}

Method's name explains it all. Once you are sure DownloadManager is available, you can do something like this:

String url = "url you want to download";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("Some descrition");
request.setTitle("Some title");
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "name-of-the-file.ext");

// get download service and enqueue file
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);

Download progress will be showing in the notification bar.

Final thoughts

First and second methods are just the tip of the iceberg. There are lots of things you have to keep in mind if you want your app to be robust. Here is a brief list:



Leave a reply



Submit