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
}
}
Answer from Eric Mill on Stack OverflowThis 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
}
}
Permission
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
Download Fuction Code
public void downloadFile() {
String DownloadUrl = audio1;
DownloadManager.Request request1 = new DownloadManager.Request(Uri.parse(DownloadUrl));
request1.setDescription("Sample Music File"); //appears the same in Notification bar while downloading
request1.setTitle("File1.mp3");
request1.setVisibleInDownloadsUi(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request1.allowScanningByMediaScanner();
request1.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
}
request1.setDestinationInExternalFilesDir(getApplicationContext(), "/File", "Question1.mp3");
DownloadManager manager1 = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
Objects.requireNonNull(manager1).enqueue(request1);
if (DownloadManager.STATUS_SUCCESSFUL == 8) {
DownloadSuccess();
}
}
Videos
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. :)
I'd like to point out that Android 2.3 (API Level 9) introduces a new system service called the DownloadManager. If you're OK with only supporting 2.3, then you should definitely use it. If not, you can either:
- Check if the
DownloadManageris available and use it if it is. If it's not (Android < 2.3), download the file yourself, for example as described by xil3. - Don't use
DownloadManagerat all, if you think it's too much work. However, I strongly believe you will benefit from its use.
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
Use this Function it save File in Download folder :
private boolean downloadTask(String url , String name) throws Exception {
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);
request.setAllowedOverRoaming(false).setTitle(name);//for mp3 title
request.setDescription("Something useful. No, really.");
if (downloadManager != null) {
downloadManager.enqueue(request);
}
//mToast(mContext, "Starting download...");
MediaScannerConnection.scanFile(MainActivity.this, new String[]{result.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("tag >>>>", "on Downlaod check it");
}
});
} catch (Exception e) {
Log.i("tag >>>> ", e.toString());
return false;
}
return true;
}
But in Manifest add some lines.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"
>
Add this line in OnCreate :
//check permission
if (Build.VERSION.SDK_INT >= 23) {
Log.i("log ", "if in Build.VERSION.SDK_INT>=23");
checkper(); //<-- this is a function
} else {
Log.i("log ", " else else in Build.VERSION.SDK_INT>=23");
}
This is checkper() Function:
private final int MY_PERMISSIONS_REQUEST_READ_CONTACTS = 1;
private void checkper() {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
} else if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
} else if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
} else if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
} else {
Log.i("log", "else in checkper()");
//your code
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED
&& grantResults[2] == PackageManager.PERMISSION_GRANTED
&& grantResults[3] == PackageManager.PERMISSION_GRANTED) {
Log.i("log", "if in onRequestPermissionsResult()");
//dar seri aval har do ra dasti ok kardim amad inja
//your code
} else {
Log.i("log", "else in onRequestPermissionsResult()");
// yeki taiiid kardi
}
}
}//switch
}//onRequestPermissionsResult
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.
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>
Prepare to download files to any directory your app owns, such as getFilesDir(), getExternalFilesDir(), getCacheDir() or getExternalCacheDir().
val privateDir = context.getFilesDir()Download file taking its progress into account (DIY):
val downloadedFile = myFancyMethodToDownloadToAnyDir(url, privateDir, fileName)Once downloaded you can make any threatment to the file if you'd like to.
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() } } }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.
You can use the Android DownloadManager.Request.
It will need the WRITE_EXTERNAL_STORAGE Persmission until Android 9. From Android 10/ Q and above it will not need any permission (it seems it handle the permission itself).
If you want to open the file afterwards, you will need the user's permission instead (also if you only want to open it within an external app (e.g. PDF-Reader).
You can use the download manager like this:
DownloadManager.Request request = new DownloadManager.Request(<download uri>);
request.addRequestHeader("Accept", "application/pdf");
// Save the file in the "Downloads" folder of SDCARD
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,filename);
DownloadManager downloadManager = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
downloadManager.enqueue(request);
This are the references: https://developer.android.com/reference/kotlin/android/app/DownloadManager.Request?hl=en#setDestinationInExternalPublicDir(kotlin.String,%20kotlin.String)
https://developer.android.com/training/data-storage