Mobile Development 13 min read

Multiple Ways to Download Files to an Android SD Card

This article explains the evolution of Android storage permissions and presents three practical approaches—direct SD‑card permission, DocumentFile API, and Storage Access Framework—for reliably downloading files to external SD cards across various Android versions.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Multiple Ways to Download Files to an Android SD Card

Android File Download to SD Card: Several Approaches

Introduction

Recent Android releases have tightened storage permissions, making it harder to download files directly to an external SD card. This guide shows how to implement a simple file download that works on different Android versions.

Permission Changes Over Android Versions

Key milestones include:

Android 5.0 – Storage Access Framework (SAF) introduced for fine‑grained file access.

Android 6.0 – Runtime permissions require requesting storage access at runtime.

Android 7.0 – File URI restrictions; apps must use ContentProvider to share files.

Android 10 – Scoped Storage limits apps to their own directories and specific public folders.

Android 11 – Further Scoped Storage tightening and the new MANAGE_EXTERNAL_STORAGE permission (restricted by Google Play).

Android 12 – More granular media‑type permissions.

Android 13/14 – Permission model stabilised; legacy flag android:requestLegacyExternalStorage="true" is obsolete.

Method 1: Direct SD‑Card Permission

The simplest solution is to request the write‑external‑storage permission and write to the public Download directory.

File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String downloadPath = downloadDir.getAbsolutePath();

MyOkhttpUtil.okHttpDownloadFile("http://www.baidu.com/income-eligibility-criteria.pdf",
    new CallBackUtil.CallBackFile(downloadPath, fileName) {
        @Override
        public void onFailure(Call call, Exception e) {
            LoadingDialogManager.get().dismissLoading();
            YYLogUtils.e("downLoadMessageFile--onFailure");
            ToastUtils.get().showFailText(CommUtils.getContext(), "File download failed");
        }
        @Override
        public void onProgress(float progress, long total) {
            super.onProgress(progress, total);
        }
        @Override
        public void onResponse(Call call, File response) {
            LoadingDialogManager.get().dismissLoading();
            YYLogUtils.w("downLoadMessageFile--Success--path=" + response.getAbsolutePath());
            ToastUtils.get().showSuccessText(CommUtils.getContext(), "File download successful, save path: " + response.getAbsolutePath());
        }
    });

This works on Android 7 devices (fails) and Android 13 devices (succeeds) when the appropriate permission is granted.

Permission Request Example

PermissionEngine.get().requestPermission(activity, new PermissionEngine.OnSuccessCallback() {
    @Override
    public void onSuccess() {
    }
}, Manifest.permission_group.STORAGE);

Or request the specific read/write permissions:

PermissionEngine.get().requestPermission(activity, new PermissionEngine.OnSuccessCallback() {
    @Override
    public void onSuccess() {
    }
}, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE);

Method 2: Using DocumentFile API

For Android 10+ where direct File access is restricted, wrap the target path with DocumentFile and write via an OutputStream obtained from the Uri.

private void downloadFile(DocumentFile selectedDir, String fileName) {
    String url = "http://example.com/file.pdf";
    File parent = new File(selectedDir.getPath());
    DocumentFile documentFile = DocumentFile.fromFile(parent);
    DocumentFile subDocumentFile = documentFile.createFile("application/pdf", fileName);
    Uri uri = subDocumentFile.getUri();

    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder().url(url).build();
    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) { }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (response.isSuccessful()) {
                InputStream inputStream = response.body().byteStream();
                OutputStream outputStream = null;
                try {
                    outputStream = getContentResolver().openOutputStream(uri);
                    if (outputStream != null) {
                        copyInputStreamToOutputStream(inputStream, outputStream);
                    }
                } finally {
                    if (inputStream != null) inputStream.close();
                    if (outputStream != null) outputStream.close();
                }
            }
        }
    });
}

private void copyInputStreamToOutputStream(InputStream inputStream, OutputStream outputStream) throws IOException {
    byte[] buffer = new byte[4096];
    int len;
    while ((len = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, len);
    }
}

This approach works on Android 10 and later for custom directories.

Method 3: Using SAF (Storage Access Framework) for User‑Chosen Folder

Let the user pick a destination folder via Intent.ACTION_OPEN_DOCUMENT_TREE , then obtain the Uri and write the file similarly to Method 2.

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
activity.startActivityForResult(intent, 1027);

Handle the result:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 1027 && resultCode == Activity.RESULT_OK) {
        if (data != null) {
            Uri treeUri = data.getData();
            DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
            if (pickedDir != null) {
                // Use pickedDir to download the file
                Uri uri = pickedDir.getUri();
                // proceed with stream copy as shown earlier
            }
        }
    }
}

Conclusion

The article reviews Android's tightening storage permissions and presents three viable solutions for writing files to external storage: direct permission request (simplest), DocumentFile API (for Android 10+), and SAF with user‑chosen folders (most flexible). Developers should avoid dangerous permissions like MANAGE_DOCUMENTS and prefer the approaches above to ensure compatibility across Android versions.

Mobile DevelopmentAndroidfile downloadPermissionssafdocumentfilesdcard
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.