Programmatic File Operations on SD Card in Android: Moving, Copying, and Deleting

Dec 01, 2025 · Programming · 14 views · 7.8

Keywords: Android | SD card | file operations | Java I/O | move copy delete

Abstract: This article provides an in-depth exploration of programmatically managing files and directories on SD cards in Android devices. It begins with essential permission configurations, then details multiple methods for moving, copying, and deleting files using standard Java I/O, including File.renameTo(), byte stream copying, and efficient FileChannel transfers. The analysis covers performance differences, use cases, and code examples for safe and effective external storage management in the Android environment.

Permission Configuration and External Storage Access

Before performing file operations on an SD card in Android, the WRITE_EXTERNAL_STORAGE permission must be declared in AndroidManifest.xml. This is essential for accessing external storage (including SD cards), ensuring the app can write, move, and delete files. The permission declaration is as follows:

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

Additionally, starting from Android 6.0 (API level 23), runtime permission requests are required. Developers should use ContextCompat.checkSelfPermission() to check permission status and request user authorization dynamically via ActivityCompat.requestPermissions().

Obtaining SD Card Path

Android provides the Environment.getExternalStorageDirectory() method to retrieve the root directory of external storage. For devices with SD cards, this typically returns the mount path of the SD card. For example:

File sdCardRoot = Environment.getExternalStorageDirectory();
String sdCardPath = sdCardRoot.getAbsolutePath(); // e.g., /storage/emulated/0

Note that some devices may emulate internal storage as external storage, so the actual path can vary. Developers should use this method as a base path, combining it with specific file paths for operations.

Implementing File Movement

Moving a file involves copying it to a target location and deleting the original. Here are two common approaches:

Using File.renameTo() Method

File.renameTo() is a simple method from the Java standard library, suitable for moving files within the same file system. Its advantage is atomicity, but it may fail for cross-volume moves. Example code:

File from = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/kaic1/imagem.jpg");
File to = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/kaic2/imagem.jpg");
boolean success = from.renameTo(to);
if (!success) {
    Log.e("FileMove", "Failed to move file");
}

Copying with Byte Streams Then Deleting

For cross-volume moves or when finer control is needed, a copy-then-delete strategy can be employed. The following function uses InputStream and OutputStream:

private void moveFile(String inputPath, String inputFile, String outputPath) throws IOException {
    File inputFileObj = new File(inputPath, inputFile);
    File outputDir = new File(outputPath);
    if (!outputDir.exists()) {
        outputDir.mkdirs();
    }
    File outputFileObj = new File(outputPath, inputFile);
    
    try (InputStream in = new FileInputStream(inputFileObj);
         OutputStream out = new FileOutputStream(outputFileObj)) {
        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        out.flush();
    }
    
    if (inputFileObj.delete()) {
        Log.i("FileMove", "Original file deleted");
    } else {
        Log.e("FileMove", "Failed to delete original file");
    }
}

File Copying Techniques

Copying files is similar to moving but retains the original. Standard Java I/O offers multiple copying mechanisms:

Basic Byte Stream Copying

Using FileInputStream and FileOutputStream for block-by-block copying, suitable for most scenarios:

private void copyFile(String inputPath, String inputFile, String outputPath) throws IOException {
    File inputFileObj = new File(inputPath, inputFile);
    File outputDir = new File(outputPath);
    if (!outputDir.exists()) {
        outputDir.mkdirs();
    }
    File outputFileObj = new File(outputPath, inputFile);
    
    try (InputStream in = new FileInputStream(inputFileObj);
         OutputStream out = new FileOutputStream(outputFileObj)) {
        byte[] buffer = new byte[8192]; // Use larger buffer for better performance
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        out.flush();
    }
}

Efficient Copying with FileChannel

For large files, the FileChannel.transferTo() or transferFrom() methods leverage zero-copy techniques at the OS level, significantly improving performance:

private void copyFileWithChannel(File source, File destination) throws IOException {
    try (FileChannel sourceChannel = new FileInputStream(source).getChannel();
         FileChannel destChannel = new FileOutputStream(destination).getChannel()) {
        sourceChannel.transferTo(0, sourceChannel.size(), destChannel);
    }
}

File and Directory Deletion

Deletion is straightforward but requires error handling and recursive directory deletion:

Deleting a Single File

private void deleteFile(String filePath, String fileName) {
    File file = new File(filePath, fileName);
    if (file.exists() && file.isFile()) {
        if (file.delete()) {
            Log.i("FileDelete", "File deleted successfully");
        } else {
            Log.e("FileDelete", "Failed to delete file");
        }
    }
}

Recursively Deleting a Directory

Deleting a directory requires removing all its subfiles and subdirectories first:

private boolean deleteDirectory(File dir) {
    if (dir.isDirectory()) {
        File[] children = dir.listFiles();
        if (children != null) {
            for (File child : children) {
                boolean success = deleteDirectory(child);
                if (!success) {
                    return false;
                }
            }
        }
    }
    return dir.delete();
}

Error Handling and Best Practices

Robust error handling is critical in file operations:

By combining standard Java I/O with Android-specific APIs, developers can manage SD card files efficiently and securely. Method selection should balance performance, compatibility, and code complexity to suit various application scenarios.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.