Android
File Management
SD Card
Assets Folder
File Copying

How to copy files from 'assets' folder to sdcard?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Android assets are packaged inside the application and are not regular files on disk, so you cannot copy them with a plain filesystem move. You must open each asset as a stream and write that stream to a destination file. On modern Android, the safest destination is usually app-specific external storage rather than a shared root of the SD card.

Understand the Destination Before Copying

Older Android examples often say "copy to SD card" as if there is one writable public directory. That advice is outdated. Current Android versions use scoped storage rules, and unrestricted writes to shared external storage are no longer the default path.

In practice you usually have three choices:

  • 'context.filesDir for private internal storage'
  • 'context.getExternalFilesDir(null) for app-specific external storage'
  • 'MediaStore for user-visible shared files such as images or downloads'

If the app just needs a model, template, or seed database that only the app will read, use getExternalFilesDir or internal storage. That avoids broad storage permissions and works consistently across current Android releases.

Copy a Single Asset With Kotlin

The basic pattern is: open an asset, create the output file, and stream bytes from one to the other.

kotlin
1import android.content.Context
2import java.io.File
3
4fun copyAsset(
5    context: Context,
6    assetName: String,
7    outputName: String
8): File {
9    val targetDir = context.getExternalFilesDir(null)
10        ?: error("External storage is unavailable")
11
12    val outputFile = File(targetDir, outputName)
13    outputFile.parentFile?.mkdirs()
14
15    context.assets.open(assetName).use { input ->
16        outputFile.outputStream().use { output ->
17            input.copyTo(output)
18        }
19    }
20
21    return outputFile
22}
kotlin
val copied = copyAsset(requireContext(), "config/default.json", "config/default.json")
println(copied.absolutePath)

The important detail is that assets does not give you a normal file path. AssetManager.open is the correct entry point.

Copy a Folder-Like Asset Tree

Assets can be arranged in nested paths, but directories inside assets are still virtual packaging entries. If you want to copy a whole bundle, recurse through the asset list and copy each leaf file.

kotlin
1import android.content.Context
2import java.io.File
3
4fun copyAssetTree(context: Context, assetPath: String, targetRoot: File) {
5    val children = context.assets.list(assetPath).orEmpty()
6
7    if (children.isEmpty()) {
8        val outFile = File(targetRoot, assetPath)
9        outFile.parentFile?.mkdirs()
10        context.assets.open(assetPath).use { input ->
11            outFile.outputStream().use { output ->
12                input.copyTo(output)
13            }
14        }
15        return
16    }
17
18    for (child in children) {
19        val childPath = if (assetPath.isEmpty()) child else "$assetPath/$child"
20        copyAssetTree(context, childPath, targetRoot)
21    }
22}

This is useful when shipping a TensorFlow Lite model plus labels, or a template directory with several configuration files.

Avoid Recopying on Every Launch

Asset copying is often done during startup, which makes repeated copying expensive. Check whether the destination file already exists before writing it again. If the asset may change between app versions, store a version marker or compare a checksum instead of blindly overwriting every run.

kotlin
1val outputDir = requireContext().getExternalFilesDir(null)!!
2val destination = File(outputDir, "models/model.tflite")
3if (!destination.exists()) {
4    copyAsset(requireContext(), "models/model.tflite", "models/model.tflite")
5}

That small guard avoids slow startup and unnecessary flash writes.

Handling Public Shared Storage

If the real requirement is to place the file where the user can browse it with other apps, app-specific external storage is not enough. In that case, use the appropriate shared-storage API for the file category. For downloads or media collections, MediaStore is the modern approach.

Do not reach for old examples that concatenate raw /sdcard/... style paths. Those paths are fragile, device-dependent, and often blocked by storage policy.

Verifying the Result

For critical assets, especially databases or ML models, verify that the file size is non-zero and that the file can be opened by the consumer immediately after copying. A startup copy that silently truncates due to low space is much harder to debug later than an explicit validation failure near the copy step.

If the file is large, also log how long the copy took. That gives you a concrete signal when startup regressions appear after adding larger bundled data.

Common Pitfalls

A common mistake is treating an asset as a normal filesystem path. Assets are packaged resources, so direct File("assets/...") logic does not work.

Another frequent issue is writing to a public external path when the app only needed app-specific storage. That creates permission and compatibility problems for no benefit.

Developers also forget to create parent directories before writing nested files. The copy then fails even though the asset is valid.

Finally, repeatedly copying large assets at startup makes the app feel slow and can produce unnecessary wear on storage. Check whether the file already exists or whether a version change actually requires replacement.

Summary

  • Read assets through AssetManager.open, not through raw file paths.
  • Prefer app-specific external storage or internal storage for app-owned files.
  • Recurse through assets.list when copying bundled directory structures.
  • Avoid copying the same large file on every launch.
  • Use shared-storage APIs only when the file must be visible outside the app.

Course illustration
Course illustration

All Rights Reserved.