You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

请求协助:实现将文件从内部存储移动到SD卡指定文件夹

解决Android将文件移动到SD卡指定自定义文件夹的问题

我明白你的困扰——之前的方法要么只能在内部存储里折腾,要么把文件丢进了应用专属的外部目录,完全达不到你想要的SD卡自定义文件夹需求。下面我一步步帮你搞定这个问题:

一、先搞定权限问题(关键前提)

Android的存储权限机制分版本,得对应处理:

  • Android 6.0(API 23)到Android 9(API 28):需要申请WRITE_EXTERNAL_STORAGE权限,记得要动态申请(不能只在Manifest里加声明)。
  • Android 10(API 29):如果要访问SD卡的自定义公共文件夹,要么申请MANAGE_EXTERNAL_STORAGE权限,要么改用MediaStore API(后者更适合媒体文件)。
  • Android 11+(API 30及以上):必须申请MANAGE_EXTERNAL_STORAGE权限,而且需要引导用户到系统设置页面手动开启(系统会限制直接弹窗申请这个权限)。

先在Manifest里加基础权限:

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

动态申请权限的核心代码示例:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    if (!Environment.isExternalStorageManager()) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
        Uri uri = Uri.fromParts("package", getPackageName(), null);
        intent.setData(uri);
        startActivityForResult(intent, 100);
    }
} else {
    ActivityCompat.requestPermissions(this, 
        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
}

二、获取真正的SD卡根路径

你之前用的ContextCompat.getExternalFilesDirs()[0]是内部存储的应用专属外部目录,[1]是SD卡的应用专属目录(比如/storage/XXXX-XXXX/Android/data/你的包名/files/),但你要的是SD卡上的自定义文件夹,所以得先拿到SD卡的根路径:

适配多版本的SD卡路径获取方法

private File getSDCardRootPath() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // Android 10+推荐用官方API获取存储卷
        for (StorageVolume volume : Environment.getExternalStorageVolumes()) {
            if (!volume.isEmulated() && volume.getState().equals(Environment.MEDIA_MOUNTED)) {
                return volume.getDirectory();
            }
        }
    } else {
        // 旧版本遍历/storage目录找SD卡
        File storageDir = new File("/storage/");
        File[] files = storageDir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory() && !file.getName().equals("emulated") 
                    && Environment.getStorageState(file).equals(Environment.MEDIA_MOUNTED)) {
                    return file;
                }
            }
        }
    }
    return null; // 未检测到SD卡或SD卡未挂载
}

这个方法会返回SD卡的根目录,比如/storage/XXXX-XXXX/(XXXX-XXXX是SD卡的UUID)。

三、创建指定文件夹并移动文件

拿到SD卡根路径后,就可以构建你想要的目标文件夹,然后移动文件。注意:跨存储分区(内部→SD卡)用File.renameTo()可能失效,所以推荐用「复制文件+删除原文件」的方式,更可靠:

核心移动代码

private boolean moveFileToSDCardCustomFolder(File sourceFile, String targetFolderName) {
    File sdCardRoot = getSDCardRootPath();
    if (sdCardRoot == null || !sdCardRoot.exists()) {
        Toast.makeText(this, "未检测到SD卡或SD卡未挂载", Toast.LENGTH_SHORT).show();
        return false;
    }

    // 构建目标文件夹路径
    File targetFolder = new File(sdCardRoot, targetFolderName);
    if (!targetFolder.exists()) {
        // 创建文件夹(包括必要的父目录)
        boolean isFolderCreated = targetFolder.mkdirs();
        if (!isFolderCreated) {
            Toast.makeText(this, "无法创建目标文件夹", Toast.LENGTH_SHORT).show();
            return false;
        }
    }

    // 构建目标文件路径
    File targetFile = new File(targetFolder, sourceFile.getName());
    if (targetFile.exists()) {
        // 如果目标文件已存在,可选择覆盖或返回失败,这里先删除旧文件
        targetFile.delete();
    }

    // 复制文件到SD卡
    try (InputStream in = new FileInputStream(sourceFile);
         OutputStream out = new FileOutputStream(targetFile)) {
        byte[] buffer = new byte[1024];
        int length;
        while ((length = in.read(buffer)) > 0) {
            out.write(buffer, 0, length);
        }
        // 复制成功后删除原文件
        sourceFile.delete();
        return true;
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "文件移动失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
        return false;
    }
}

调用示例

假设你的源文件是内部存储的SampleFile.txt

File sourceFile = new File(getFilesDir(), "SampleFile.txt"); // 内部存储的目标文件
boolean isSuccess = moveFileToSDCardCustomFolder(sourceFile, "MyFileStorage");
if (isSuccess) {
    Toast.makeText(this, "文件移动成功", Toast.LENGTH_SHORT).show();
}

四、重要提醒

  1. 如果你的目标文件夹是SD卡上的公共目录(比如Download、Documents),可以不用手动创建,直接用Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)获取路径,这样在Android 10+可以不用申请MANAGE_EXTERNAL_STORAGE权限(但普通自定义文件夹还是需要)。
  2. 测试时确保SD卡已正确挂载,并且有足够的存储空间。
  3. Android 13+如果移动的是媒体文件,还需要额外申请READ_MEDIA_IMAGES/READ_MEDIA_VIDEO等权限。

内容的提问来源于stack exchange,提问作者Komal

火山引擎 最新活动