Android写入外置sd卡

android9.0保存文件到外置Sd卡
1. 获取外置sd卡根目录
sd卡根目录是类似/storage/0000-0000 这样的路径, 
而用runtime.exec("mount"), 或者是用StorageManager.getStorageVolumes()再用反射path获取的路径是/mnt/media_rw/0000-0000 不可用,需要用下面的方式获取:

    public String getExternalDir(Context context) {
        String root = null;
        File[] dirs = context.getExternalFilesDirs(null);
        for (File f : dirs) {
            String path = f.getAbsolutePath();
            if (!path.contains(Environment.getExternalStorageDirectory().getAbsolutePath())) {
                int index = path.indexOf("/Android/data");
                if (index != -1) {
                    root = path.substring(0, index);
                    break;
                }
            }
        }
        return root;
    }

 2. 外置sd卡写入权限检查
 外置sd卡用File.canWrite()检查返回均为false,因此无法用来检查是否可写。
 看之前有人写的判断方式:
    if (!file.isDirectory()) {
        writable = file.createNewFile() && file.delete();
    } else {
        writable = file.mkdirs() && file.delete();
    }
 测试是否可写(测过不好用,建议用下面的); 或者把申请权限回调的Uri保存,判断是否存在及对应的DocumentFile是否可写

     boolean hasPermission = false;
    String uriStr = EdoPreference.getExternalUri();//fetch value in sharedpreferences
    if (uriStr != null) {
        DocumentFile documentFile = DocumentFile.fromTreeUri(context, Uri.parse(uriStr));
        if (documentFile != null) {
            hasPermission = documentFile.canWrite();
        }
    }

3. 申请外置sd卡写入权限
如果>=7.0, 需要用StorageVolume.createAccessIntent()来创建intent,否则intent需设置action: Intent.ACTION_OPEN_DOCUMENT_TREE

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        StorageManager storageManager = EmailApplication.getContext().getSystemService(StorageManager.class);
        StorageVolume volume = storageManager.getStorageVolume(new File(externalDir));
        if (volume != null) {
            Intent accessIntent = volume.createAccessIntent(null);
            if (accessIntent != null) {
                extFragment.startActivityForResult(accessIntent, ExtSdFragment.REQ_CODE_EXT);
            }
        }
    } else {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
        extFragment.startActivityForResult(intent, ExtSdFragment.REQ_CODE_EXT);
    }

其中externalDir是sd卡的任意路径, 在onActivityResult回调中保存Uri的string,以备后用
4. 写入外置sd卡
直接用FileOutputStream写入外置sd卡会失败,原因是对应file的canWrite()返回false, 用户授权后仍然是false(这里太坑了!!!),需要用DocumentFile获取Uri,然后用Uri打开OutputStream:

    DocumentFile file = getExtDocumentFile(to.substring(externalDir.length() + 1));
    if (file == null) {
        return false;
    }
    try (FileInputStream is = new FileInputStream(from); OutputStream os = EmailApplication.getContext().getContentResolver().openOutputStream(file.getUri())) {
        byte[] b = new byte[is.available()];
        is.read(b);
        os.write(b);
        return true;
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }

//根据文件路径获取DocumentFile,需要一层层去找

    private DocumentFile getExtDocumentFile(String path,Context context) {
        String extSDUri = EdoPreference.getExternalUri();//获取保存的Uri string
        if (extSDUri == null) {
            return null;
        }
        DocumentFile file = DocumentFile.fromTreeUri(context, Uri.parse(extSDUri));
        if (file == null) {
            return null;
        }
        if (path.contains("/")) {
            String[] split = path.split("/");
            for (int i = 0; i < split.length; i++) {
                DocumentFile df = file.findFile(split[i]);
                if (df == null) {
                    if (i == split.length - 1) {
                        df = file.createFile("*/*", split[i]);
                    } else {
                        df = file.createDirectory(split[i]);
                    }
                }
                file = df;
            }
        } else {
            file = file.createFile("*/*", path);
        }
        return file;
    }


 
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值