Android 对于外部TF(SD)卡和U盘的读写操作

1 篇文章 0 订阅
1 篇文章 0 订阅

首先读取外置TF卡,需要获取到外置TF卡的权限,因为Android 在4.4版本之后 引入了外部存储访问的框架(SAF)。SAF其中的部分功能就是通过其获取对外置TF卡的读写权限,从而操作外置TF卡。

 

目录

1、TF卡读写操作

1.1获取TF卡权限

1.2、DoucumentFile简介

1.2.1创建文件

1.2.2编辑文件

1.2.3查找文件

1.2.4删除文件

1.2.5获得文件名

1.2.6获得文件Uri

1.2.7获得文件类型(MIME类型)

1.2.8创建文件夹

1.2.9获得文件的父文件

1.2.10查看是否是目录/文件

1.2.11获得目录下所有文件

1.2.12重命名文件

2、U盘的读取

3、TF卡路径和U盘根路径获取

附件

MIME_TYPE

StorageUtils.java

DocumentsUtils.java

MainActivity.java


在Android7.0时对外部存储进行了简化

1、TF卡读写操作

1.1获取TF卡权限

private void showOpenDocumentTree(String rootPath) {
    Intent intent = null;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        StorageManager sm = getSystemService(StorageManager.class);
         StorageVolume volume = sm.getStorageVolume(new File(rootPath));
         if (volume != null) {
             intent = volume.createAccessIntent(null);
         }
    }
    if (intent == null) {
        intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    }
    Log.d(TAG,"startActivityForResult...");
    startActivityForResult(intent, DocumentsUtils.OPEN_DOCUMENT_TREE_CODE);
}

当我们应用在打开的时候就会提示如下的弹框

点击确定,在对权限回调进行处理,将SD卡路径和URI通过Sp存储起来方便下次使用。SD卡路径的获取会在后文中提到。

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case DocumentsUtils.OPEN_DOCUMENT_TREE_CODE:
            if (data != null && data.getData() != null) {
                Uri uri = data.getData();
                String rootPath = StorageUtils.getSDCardDir(this);
                DocumentsUtils.saveTreeUri(this, rootPath, uri);
                final int takeFlags = data.getFlags()
                        & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                Log.d(TAG, "save tree uri = " + uri);
                // 相当于将url存储下来,下次开启apk不在进行弹框权限请求
                getContentResolver().takePersistableUriPermission(uri, takeFlags);
            }
            break;
     }
 }

1.2、DoucumentFile简介

        在使用之前,我们要知道这是一个模拟File的程序类,它提供了文档树的模式,所以它会有很大的开销,为了节省我们的空间,我们要确定是不是要访问整个文档树,最小特权原则规定只应要求访问真正需要的文档。如果只需要用户选择单个文件,使用ACTION_OPEN_DOCUMENT或 ACTION_GET_CONTENT。如果想让用户选择多个文件,添加EXTRA_ALLOW_MULTIPLE。如果只需要用户保存单个文件,使用ACTION_CREATE_DOCUMENT。如果使用这些API,可以通过产生的getData()成 fromSingleUri(Context, Uri)与文档工作。

如果确实需要完全访问整个文档子树,首先启动ACTION_OPEN_DOCUMENT_TREE以允许用户选择目录。然后,通过所产生的getData()进入 fromTreeUri(Context, Uri)开始与用户选择的树工作。

在导航DocumentFile实例树时,始终可以使用 getUri()获取表示该对象的基础文档的Uri,以便与之一起使openInputStream(Uri)等。

1.2.1创建文件

DocumentFile documentFile = DocumentFile.fromTreeUri(this, uri);

DocumentFile newFile = documentFile.createFile("text/plain", "hello.txt");

通过 createFile(String mimeType, String displayName)来创建一个hello.txt的文件.

对应这里的 “mimeType” 我会单独在文末给贴出来。

注:因为我获得uri权限是sd卡完整权限,所以创建文件的位置是卡的跟目录.能使用uri就用uri,不要documentFile一层套一层,很浪费资源的。

1.2.2编辑文件

向hello.txt写入string

OutputStream out = null;
try {
    out = context.getContentResolver().openOutputStream(fileText.getUri());
    out.write("A long time ago...".getBytes());
    out.close();
} catch (Exception e) {
    e.printStackTrace();
}

写入一个图片文件

OutputStream out = null;
try {
    out = context.getContentResolver().openOutputStream(fileImage.getUri());
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher).compress(Bitmap.CompressFormat.JPEG, 100, baos);
    InputStream inputimage = new ByteArrayInputStream(baos.toByteArray());
    byte[] bytes = new byte[1024 * 10];
    int len = 0;
    while ((len = inputimage.read(bytes)) != -1) {
        out.write(bytes, 0, len);
    }
    out.close();
} catch (Exception e) {
    e.printStackTrace();
}

1.2.3查找文件

DocumentFile file = documentFile.findFile("abc.txt");

1.2.4删除文件

DocumentFile有delete()方法来删除文件

newFile.delete();

1.2.5获得文件名

通过getName()能够得到当前documentFile的文件名

Log.d(TAG, "newFile.getName():"+newFile.getName());

1.2.6获得文件Uri

Log.d(TAG, "newFile.getUri():"+newFile.getUri());

1.2.7获得文件类型(MIME类型)

Log.d(TAG, "newFile.getType():"+newFile.getType());

1.2.8创建文件夹

createDirectory(String displayName)能够在当前documentFile树下创建名称为displayName的文件夹

DocumentFile testDir = documentFile.createDirectory("helloDir");

1.2.9获得文件的父文件

getParentFile()能够获得当前文件的父文件

Log.d(TAG, "newFile.getParentFile():"+newFile.getParentFile());

1.2.10查看是否是目录/文件

boolean directory = documentFile.isDirectory();

boolean file = documentFile.isFile();

1.2.11获得目录下所有文件

DocumentFile[] documentFiles = documentFile.listFiles();

1.2.12重命名文件

boolean renameFile = newFile.renameTo("renameFile");

 

代码中提到了DoucumentsFile类,这个类比较好找,网上一大堆,文末会附上代码

之前查看了网上其他博客,发现用他们的方法不生效或者崩溃.对我来说 getTreeUri 和 setTreeUri是比较有用的,同时也加了一部分自己的方法方便使用 (doCopyFile)

/**
 * @param context      context
 * @param uri          uri 默认传null ,从sp中读取
 * @param orgFilePath  被复制的文件路径
 * @param destFileName 复制后的文件名
 * @desc 将orgFilePath文件复制到指定SD卡指定路径/storage/xx-xx/hello/
 */
public static boolean doCopyFile(Context context, Uri uri, String orgFilePath, String destFileName)

在配合MainActivity这样操作就可以把东西写在外置TF卡里了

public void onClick1(View v) {
    String rootPath = StorageUtils.getSDCardDir(this);
    String urlStr = DocumentsUtils.getTreeUri(this, rootPath);
    Uri uri = Uri.parse(urlStr);
    writeFile(this, uri);
    DocumentsUtils.doCopyFile(this,null,"/storage/emulated/0/helloWorld.apk","copyApk.apk");
}

有两种方式

  • 是直接穿件文件往文件里写,writeFile
  • 是对已有的文件copy到TF卡里,doCopyFile,因为有时候文件不好直接往TF卡中操作的时候,可以现在本地创建好,之后再进行复制就可以了

操作前

操作后

复制的APK 亲测可安装,这样完美操作TF卡。

2、U盘的读取

U盘文件的读取相对简单一些,主要是需要获取到U盘的根路径,然后通过File的操作来操作U盘,不用必须通过广播的方式来读取U盘。

public void onClick(View v) {
        String path = StorageUtils.getUsbDir(this);
        if (!TextUtils.isEmpty(path)) {
            File[] files = new File(path).listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.getName().endsWith(".zip")) {
                        Log.d(TAG, "file path=" + file.getAbsolutePath());
                    }
                }
            }
        } else {
            Log.d(TAG, "没有插入U盘");
        }
    }

3、TF卡路径和U盘根路径获取

TF卡路径获取方法

public static String getSDCardDir(Context context) {
        String sdcardDir = null;
        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        Class<?> volumeInfoClazz = null;
        Class<?> diskInfoClazz = null;
        try {
            diskInfoClazz = Class.forName("android.os.storage.DiskInfo");
            Method isSd = diskInfoClazz.getMethod("isSd");
            volumeInfoClazz = Class.forName("android.os.storage.VolumeInfo");
            Method getType = volumeInfoClazz.getMethod("getType");
            Method getDisk = volumeInfoClazz.getMethod("getDisk");
            Field path = volumeInfoClazz.getDeclaredField("path");
            Method getVolumes = storageManager.getClass().getMethod("getVolumes");
            List<Class<?>> result = (List<Class<?>>) getVolumes.invoke(storageManager);
            for (int i = 0; i < result.size(); i++) {
                Object volumeInfo = result.get(i);
                if ((int) getType.invoke(volumeInfo) == 0) {
                    Object disk = getDisk.invoke(volumeInfo);
                    if (disk != null) {
                        if ((boolean) isSd.invoke(disk)) {
                            sdcardDir = (String) path.get(volumeInfo);
                            break;
                        }
                    }
                }
            }
            if (sdcardDir == null) {
                Log.w(TAG, "sdcardDir null");
                return null;
            } else {
                Log.i(TAG, "sdcardDir " + sdcardDir + File.separator);
                return sdcardDir + File.separator;
            }
        } catch (Exception e) {
            Log.i(TAG, "sdcardDir e " + e.getMessage());
            e.printStackTrace();
        }
        Log.w(TAG, "sdcardDir null");
        return null;
    }

U盘根路径获取

public static String getUsbDir(Context context) {
        String sdcardDir = null;
        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        Class<?> volumeInfoClazz = null;
        Class<?> diskInfoClazz = null;
        try {
            diskInfoClazz = Class.forName("android.os.storage.DiskInfo");
            Method isUsb = diskInfoClazz.getMethod("isUsb");
            volumeInfoClazz = Class.forName("android.os.storage.VolumeInfo");
            Method getType = volumeInfoClazz.getMethod("getType");
            Method getDisk = volumeInfoClazz.getMethod("getDisk");
            Field path = volumeInfoClazz.getDeclaredField("path");
            Method getVolumes = storageManager.getClass().getMethod("getVolumes");
            List<Class<?>> result = (List<Class<?>>) getVolumes.invoke(storageManager);
            for (int i = 0; i < result.size(); i++) {
                Object volumeInfo = result.get(i);
                Log.w(TAG, "disk path " + path.get(volumeInfo));
                if ((int) getType.invoke(volumeInfo) == 0) {
                    Object disk = getDisk.invoke(volumeInfo);
                    Log.w(TAG, "is usb " + isUsb.invoke(disk));
                    if (disk != null) {
                        if ((boolean) isUsb.invoke(disk)) {
                            sdcardDir = (String) path.get(volumeInfo);
                            break;
                        }
                    }
                }
            }
            if (sdcardDir == null) {
                Log.w(TAG, "usbpath null");
                return null;
            } else {
                Log.i(TAG, "usbpath " + sdcardDir + File.separator);
                return sdcardDir + File.separator;
            }
        } catch (Exception e) {
            Log.i(TAG, "usbpath e " + e.getMessage());
            e.printStackTrace();
        }
        Log.w(TAG, "usbpath null");
        return null;
    }

两者代码大同小异,小异在什么地方,看这里

Method isUsb = diskInfoClazz.getMethod("isUsb");

这里的反射调用为什么这么用,我们可以参考Android源码中的 “DiskInfo.java” 和 “VolumeInfo.java”,系统通过isUsb和isSd变量来区别是否是U盘设备和外接SD卡设备。

最后不能忘记最基础的读写权限

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

附件

MIME_TYPE

{".3gp",    "video/3gpp"},
{".apk",    "application/vnd.android.package-archive"},
{".asf",    "video/x-ms-asf"},
{".avi",    "video/x-msvideo"},
{".bin",    "application/octet-stream"},
{".bmp",    "image/bmp"},
{".c",  "text/plain"},
{".class",  "application/octet-stream"},
{".conf",   "text/plain"},
{".cpp",    "text/plain"},
{".doc",    "application/msword"},
{".docx",   "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
{".xls",    "application/vnd.ms-excel"},
{".xlsx",   "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{".exe",    "application/octet-stream"},
{".gif",    "image/gif"},
{".gtar",   "application/x-gtar"},
{".gz", "application/x-gzip"},
{".h",  "text/plain"},
{".htm",    "text/html"},
{".html",   "text/html"},
{".jar",    "application/java-archive"},
{".java",   "text/plain"},
{".jpeg",   "image/jpeg"},
{".jpg",    "image/jpeg"},
{".js", "application/x-javascript"},
{".log",    "text/plain"},
{".m3u",    "audio/x-mpegurl"},
{".m4a",    "audio/mp4a-latm"},
{".m4b",    "audio/mp4a-latm"},
{".m4p",    "audio/mp4a-latm"},
{".m4u",    "video/vnd.mpegurl"},
{".m4v",    "video/x-m4v"},
{".mov",    "video/quicktime"},
{".mp2",    "audio/x-mpeg"},
{".mp3",    "audio/x-mpeg"},
{".mp4",    "video/mp4"},
{".mpc",    "application/vnd.mpohun.certificate"},
{".mpe",    "video/mpeg"},
{".mpeg",   "video/mpeg"},
{".mpg",    "video/mpeg"},
{".mpg4",   "video/mp4"},
{".mpga",   "audio/mpeg"},
{".msg",    "application/vnd.ms-outlook"},
{".ogg",    "audio/ogg"},
{".pdf",    "application/pdf"},
{".png",    "image/png"},
{".pps",    "application/vnd.ms-powerpoint"},
{".ppt",    "application/vnd.ms-powerpoint"},
{".pptx",   "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
{".prop",   "text/plain"},
{".rc", "text/plain"},
{".rmvb",   "audio/x-pn-realaudio"},
{".rtf",    "application/rtf"},
{".sh", "text/plain"},
{".tar",    "application/x-tar"},
{".tgz",    "application/x-compressed"},
{".txt",    "text/plain"},
{".wav",    "audio/x-wav"},
{".wma",    "audio/x-ms-wma"},
{".wmv",    "audio/x-ms-wmv"},
{".wps",    "application/vnd.ms-works"},
{".xml",    "text/plain"},
{".z",  "application/x-compress"},
{".zip",    "application/x-zip-compressed"},
{"",        "*/*"}

StorageUtils.java

import android.content.Context;
import android.os.StatFs;
import android.os.storage.StorageManager;
import android.util.Log;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

public class StorageUtils {
    private static final String TAG = "StorageUtils";

    //定义GB的计算常量
    private static final int GB = 1024 * 1024 * 1024;
    //定义MB的计算常量
    private static final int MB = 1024 * 1024;
    //定义KB的计算常量
    private static final int KB = 1024;

    public static String bytes2kb(long bytes) {
        //格式化小数
        DecimalFormat format = new DecimalFormat("###.0");
        if (bytes / GB >= 1) {
            return format.format(bytes * 1.0f / GB) + "GB";
        } else if (bytes / MB >= 1) {
            return format.format(bytes * 1.0f / MB) + "MB";
        } else if (bytes / KB >= 1) {
            return format.format(bytes * 1.0f / KB) + "KB";
        } else {
            return bytes + "B";
        }
    }

    /*
    获取全部存储设备信息封装对象
     */
    public static ArrayList<Volume> getVolume(Context context) {
        ArrayList<Volume> list_storagevolume = new ArrayList<Volume>();

        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);

        try {
            Method method_volumeList = StorageManager.class.getMethod("getVolumeList");

            method_volumeList.setAccessible(true);

            Object[] volumeList = (Object[]) method_volumeList.invoke(storageManager);
            if (volumeList != null) {
                Volume volume;
                for (int i = 0; i < volumeList.length; i++) {
                    try {
                        volume = new Volume();
                        volume.setPath((String) volumeList[i].getClass().getMethod("getPath").invoke(volumeList[i]));
                        volume.setRemovable((boolean) volumeList[i].getClass().getMethod("isRemovable").invoke(volumeList[i]));
                        volume.setState((String) volumeList[i].getClass().getMethod("getState").invoke(volumeList[i]));
                        list_storagevolume.add(volume);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                }
            } else {
                Log.e("null", "-null-");
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return list_storagevolume;
    }

    /**
     * SD卡剩余空间
     */
    public static long getSDFreeSize(String path) {
        try {
            // 取得SD卡文件路径
            StatFs sf = new StatFs(path);
            // 获取单个数据块的大小(Byte)
            long blockSize = sf.getBlockSizeLong();
            // 空闲的数据块的数量
            long freeBlocks = sf.getAvailableBlocksLong();
            // 返回SD卡空闲大小
            // return freeBlocks * blockSize; //单位Byte
            // return (freeBlocks * blockSize)/1024; //单位KB
            return (freeBlocks * blockSize);  // 单位GB
        } catch (IllegalArgumentException e) {
            Log.d(TAG, "hello world");
        }
        return 0L;
    }

    /**
     * SD卡总容量
     */
    public static long getSDAllSize(String path) {
        try { // 取得SD卡文件路径
            StatFs sf = new StatFs(path);
            // 获取单个数据块的大小(Byte)
            long blockSize = sf.getBlockSizeLong();
            // 获取所有数据块数
            long allBlocks = sf.getBlockCountLong();
            // 返回SD卡大小
            // return allBlocks * blockSize; //单位Byte
            // return (allBlocks * blockSize)/1024; //单位KB
            return (allBlocks * blockSize); // 单位GB
        } catch (IllegalArgumentException e) {
            Log.d(TAG, "hello world");
        }
        return 0L;
    }

    public static String getSDCardDir(Context context) {
        String sdcardDir = null;
        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        Class<?> volumeInfoClazz = null;
        Class<?> diskInfoClazz = null;
        try {
            diskInfoClazz = Class.forName("android.os.storage.DiskInfo");
            Method isSd = diskInfoClazz.getMethod("isSd");
            volumeInfoClazz = Class.forName("android.os.storage.VolumeInfo");
            Method getType = volumeInfoClazz.getMethod("getType");
            Method getDisk = volumeInfoClazz.getMethod("getDisk");
            Field path = volumeInfoClazz.getDeclaredField("path");
            Method getVolumes = storageManager.getClass().getMethod("getVolumes");
            List<Class<?>> result = (List<Class<?>>) getVolumes.invoke(storageManager);
            for (int i = 0; i < result.size(); i++) {
                Object volumeInfo = result.get(i);
                if ((int) getType.invoke(volumeInfo) == 0) {
                    Object disk = getDisk.invoke(volumeInfo);
                    if (disk != null) {
                        if ((boolean) isSd.invoke(disk)) {
                            sdcardDir = (String) path.get(volumeInfo);
                            break;
                        }
                    }
                }
            }
            if (sdcardDir == null) {
                Log.w(TAG, "sdcardDir null");
                return null;
            } else {
                Log.i(TAG, "sdcardDir " + sdcardDir + File.separator);
                return sdcardDir + File.separator;
            }
        } catch (Exception e) {
            Log.i(TAG, "sdcardDir e " + e.getMessage());
            e.printStackTrace();
        }
        Log.w(TAG, "sdcardDir null");
        return null;
    }

    public static String getUsbDir(Context context) {
        String sdcardDir = null;
        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        Class<?> volumeInfoClazz = null;
        Class<?> diskInfoClazz = null;
        try {
            diskInfoClazz = Class.forName("android.os.storage.DiskInfo");
            Method isUsb = diskInfoClazz.getMethod("isUsb");
            volumeInfoClazz = Class.forName("android.os.storage.VolumeInfo");
            Method getType = volumeInfoClazz.getMethod("getType");
            Method getDisk = volumeInfoClazz.getMethod("getDisk");
            Field path = volumeInfoClazz.getDeclaredField("path");
            Method getVolumes = storageManager.getClass().getMethod("getVolumes");
            List<Class<?>> result = (List<Class<?>>) getVolumes.invoke(storageManager);
            for (int i = 0; i < result.size(); i++) {
                Object volumeInfo = result.get(i);
                Log.w(TAG, "disk path " + path.get(volumeInfo));
                if ((int) getType.invoke(volumeInfo) == 0) {
                    Object disk = getDisk.invoke(volumeInfo);
                    Log.w(TAG, "is usb " + isUsb.invoke(disk));
                    if (disk != null) {
                        if ((boolean) isUsb.invoke(disk)) {
                            sdcardDir = (String) path.get(volumeInfo);
                            break;
                        }
                    }
                }
            }
            if (sdcardDir == null) {
                Log.w(TAG, "usbpath null");
                return null;
            } else {
                Log.i(TAG, "usbpath " + sdcardDir + File.separator);
                return sdcardDir + File.separator;
            }
        } catch (Exception e) {
            Log.i(TAG, "usbpath e " + e.getMessage());
            e.printStackTrace();
        }
        Log.w(TAG, "usbpath null");
        return null;
    }

    /*
     存储设备信息封装类
     */
    public static class Volume {
        protected String path;
        protected boolean removable;
        protected String state;

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public boolean isRemovable() {
            return removable;
        }

        public void setRemovable(boolean removable) {
            this.removable = removable;
        }

        public String getState() {
            return state;
        }

        public void setState(String state) {
            this.state = state;
        }
    }
}

DocumentsUtils.java

import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.preference.PreferenceManager;
import android.provider.DocumentsContract;
import android.support.v4.provider.DocumentFile;
import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

public class DocumentsUtils {

    private static final String TAG = DocumentsUtils.class.getSimpleName();

    public static final int OPEN_DOCUMENT_TREE_CODE = 8000;

    private static List<String> sExtSdCardPaths = new ArrayList<>();

    private DocumentsUtils() {

    }

    public static void cleanCache() {
        sExtSdCardPaths.clear();
    }

    /**
     * Get a list of external SD card paths. (Kitkat or higher.)
     *
     * @return A list of external SD card paths.
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    private static String[] getExtSdCardPaths(Context context) {
        if (sExtSdCardPaths.size() > 0) {
            return sExtSdCardPaths.toArray(new String[0]);
        }
        for (File file : context.getExternalFilesDirs("external")) {
            if (file != null && !file.equals(context.getExternalFilesDir("external"))) {
                int index = file.getAbsolutePath().lastIndexOf("/Android/data");
                if (index < 0) {
                    Log.w(TAG, "Unexpected external file dir: " + file.getAbsolutePath());
                } else {
                    String path = file.getAbsolutePath().substring(0, index);
                    try {
                        path = new File(path).getCanonicalPath();
                    } catch (IOException e) {
                        // Keep non-canonical path.
                    }
                    sExtSdCardPaths.add(path);
                }
            }
        }
        if (sExtSdCardPaths.isEmpty()) sExtSdCardPaths.add("/storage/sdcard1");
        return sExtSdCardPaths.toArray(new String[0]);
    }

    /**
     * Determine the main folder of the external SD card containing the given file.
     *
     * @param file the file.
     * @return The main folder of the external SD card containing this file, if the file is on an SD
     * card. Otherwise,
     * null is returned.
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    private static String getExtSdCardFolder(final File file, Context context) {
        String[] extSdPaths = getExtSdCardPaths(context);
        try {
            for (int i = 0; i < extSdPaths.length; i++) {
                if (file.getCanonicalPath().startsWith(extSdPaths[i])) {
                    return extSdPaths[i];
                }
            }
        } catch (IOException e) {
            return null;
        }
        return null;
    }

    /**
     * Determine if a file is on external sd card. (Kitkat or higher.)
     *
     * @param file The file.
     * @return true if on external sd card.
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static boolean isOnExtSdCard(final File file, Context c) {
        return getExtSdCardFolder(file, c) != null;
    }

    /**
     * Get a DocumentFile corresponding to the given file (for writing on ExtSdCard on Android 5).
     * If the file is not
     * existing, it is created.
     *
     * @param file        The file.
     * @param isDirectory flag indicating if the file should be a directory.
     * @return The DocumentFile
     */
    public static DocumentFile getDocumentFile(final File file, final boolean isDirectory,
                                               Context context) {

        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
            return DocumentFile.fromFile(file);
        }

        String baseFolder = getExtSdCardFolder(file, context);
        boolean originalDirectory = false;
        if (baseFolder == null) {
            return null;
        }

        String relativePath = null;
        try {
            String fullPath = file.getCanonicalPath();
            if (!baseFolder.equals(fullPath)) {
                relativePath = fullPath.substring(baseFolder.length() + 1);
            } else {
                originalDirectory = true;
            }
        } catch (IOException e) {
            return null;
        } catch (Exception f) {
            originalDirectory = true;
            //continue
        }
        String as = PreferenceManager.getDefaultSharedPreferences(context).getString(baseFolder,
                null);

        Uri treeUri = null;
        if (as != null) treeUri = Uri.parse(as);
        if (treeUri == null) {
            return null;
        }

        // start with root of SD card and then parse through document tree.
        DocumentFile document = DocumentFile.fromTreeUri(context, treeUri);
        if (originalDirectory) return document;
        String[] parts = relativePath.split("/");
        for (int i = 0; i < parts.length; i++) {
            DocumentFile nextDocument = document.findFile(parts[i]);

            if (nextDocument == null) {
                if ((i < parts.length - 1) || isDirectory) {
                    nextDocument = document.createDirectory(parts[i]);
                } else {
                    nextDocument = document.createFile("image", parts[i]);
                }
            }
            document = nextDocument;
        }

        return document;
    }

    public static boolean mkdirs(Context context, File dir) {
        boolean res = dir.mkdirs();
        if (!res) {
            if (DocumentsUtils.isOnExtSdCard(dir, context)) {
                DocumentFile documentFile = DocumentsUtils.getDocumentFile(dir, true, context);
                res = documentFile != null && documentFile.canWrite();
            }
        }
        return res;
    }

    public static boolean delete(Context context, File file) {
        boolean ret = file.delete();

        if (!ret && DocumentsUtils.isOnExtSdCard(file, context)) {
            DocumentFile f = DocumentsUtils.getDocumentFile(file, false, context);
            if (f != null) {
                ret = f.delete();
            }
        }
        return ret;
    }

    public static boolean canWrite(File file) {
        boolean res = file.exists() && file.canWrite();

        if (!res && !file.exists()) {
            try {
                if (!file.isDirectory()) {
                    res = file.createNewFile() && file.delete();
                } else {
                    res = file.mkdirs() && file.delete();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return res;
    }

    public static boolean canWrite(Context context, File file) {
        boolean res = canWrite(file);

        if (!res && DocumentsUtils.isOnExtSdCard(file, context)) {
            DocumentFile documentFile = DocumentsUtils.getDocumentFile(file, true, context);
            res = documentFile != null && documentFile.canWrite();
        }
        return res;
    }

    public static boolean renameTo(Context context, File src, File dest) {
        boolean res = src.renameTo(dest);

        if (!res && isOnExtSdCard(dest, context)) {
            DocumentFile srcDoc;
            if (isOnExtSdCard(src, context)) {
                srcDoc = getDocumentFile(src, false, context);
            } else {
                srcDoc = DocumentFile.fromFile(src);
            }
            DocumentFile destDoc = getDocumentFile(dest.getParentFile(), true, context);
            if (srcDoc != null && destDoc != null) {
                try {
                    if (src.getParent().equals(dest.getParent())) {
                        res = srcDoc.renameTo(dest.getName());
                    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                        res = DocumentsContract.moveDocument(context.getContentResolver(),
                                srcDoc.getUri(),
                                srcDoc.getParentFile().getUri(),
                                destDoc.getUri()) != null;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        return res;
    }

    public static InputStream getInputStream(Context context, File destFile) {
        InputStream in = null;
        try {
            if (!canWrite(destFile) && isOnExtSdCard(destFile, context)) {
                DocumentFile file = DocumentsUtils.getDocumentFile(destFile, false, context);
                if (file != null && file.canWrite()) {
                    in = context.getContentResolver().openInputStream(file.getUri());
                }
            } else {
                in = new FileInputStream(destFile);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return in;
    }

    public static OutputStream getOutputStream(Context context, File destFile) {
        OutputStream out = null;
        try {
            if (!canWrite(destFile) && isOnExtSdCard(destFile, context)) {
                DocumentFile file = DocumentsUtils.getDocumentFile(destFile, false, context);
                if (file != null && file.canWrite()) {
                    out = context.getContentResolver().openOutputStream(file.getUri());
                }
            } else {
                out = new FileOutputStream(destFile);

            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return out;
    }

    public static boolean saveTreeUri(Context context, String rootPath, Uri uri) {
        DocumentFile file = DocumentFile.fromTreeUri(context, uri);
        Log.d(TAG, "saveTreeUri= " + uri);
        if (file != null && file.canWrite()) {
            SharedPreferences perf = PreferenceManager.getDefaultSharedPreferences(context);
            perf.edit().putString(rootPath, uri.toString()).apply();
            return true;
        } else {
            Log.e(TAG, "no write permission: " + rootPath);
        }
        Log.d(TAG, "save_tree_uri=false");
        return false;
    }

    public static String getTreeUri(Context context, String rootPath) {
        SharedPreferences perf = PreferenceManager.getDefaultSharedPreferences(context);
        String treeUri = perf.getString(rootPath, "");
        Log.d(TAG, "treeUri=" + treeUri);
        return treeUri;
    }

    public static boolean checkWritableRootPath(Context context, String rootPath) {
        File root = new File(rootPath);
        if (!root.canWrite()) {
            if (DocumentsUtils.isOnExtSdCard(root, context)) {
                DocumentFile documentFile = DocumentsUtils.getDocumentFile(root, true, context);
                return documentFile == null || !documentFile.canWrite();
            } else {
                SharedPreferences perf = PreferenceManager.getDefaultSharedPreferences(context);
                String documentUri = perf.getString(rootPath, "");
                if (documentUri == null || documentUri.isEmpty()) {
                    return true;
                } else {
                    DocumentFile file = DocumentFile.fromTreeUri(context, Uri.parse(documentUri));
                    return !(file != null && file.canWrite());
                }
            }
        }
        return false;
    }

    public static boolean isExist(DocumentFile documentFile, String fileName) {
        boolean exist = false;
        if (documentFile != null) {
            DocumentFile file = documentFile.findFile(fileName);
            if (file != null) {
                exist = file.exists();
            }
        } else {
            Log.w(TAG, "documentFile null");
        }
        return exist;
    }

    /**
     * 在上一级目录下创建目录
     *
     * @param parentDir 父节点目录 docFile
     * @param dirName   child 目录名
     */
    public static DocumentFile mkdir(DocumentFile parentDir, String dirName) {
        if (parentDir != null && !TextUtils.isEmpty(dirName)) {
            if (!DocumentsUtils.isExist(parentDir, dirName)) {
                return parentDir.createDirectory(dirName);
            } else {
                return parentDir.findFile(dirName);
            }
        }
        Log.d(TAG, "dirName = " + dirName);
        return null;
    }

    /**
     * @param context      context
     * @param uri          uri 默认传null ,从sp中读取
     * @param orgFilePath  被复制的文件路径
     * @param destFileName 复制后的文件名
     * @desc 将orgFilePath文件复制到指定SD卡指定路径/storage/xx-xx/hello/
     */
    public static boolean doCopyFile(Context context, Uri uri, String orgFilePath, String destFileName) {
        // 初始化基础目录,确保录音所在目录存在
        // 获取到根目录的uri
        if (uri == null) {
            String sdPath = StorageUtils.getSDCardDir(context);
            if (TextUtils.isEmpty(sdPath)) {
                return false;
            }
            String uriStr = DocumentsUtils.getTreeUri(context, sdPath);
            if (TextUtils.isEmpty(uriStr)) {
                return false;
            }
            uri = Uri.parse(uriStr);
        }
        DocumentFile destFile = null;
        File inFile = new File(orgFilePath);
        DocumentFile documentFile = DocumentFile.fromTreeUri(context, uri);
        if (documentFile != null) {
            DocumentFile[] documentFiles = documentFile.listFiles();
            for (DocumentFile file : documentFiles) {
                Log.i(TAG, "file.getName() = " + file.getName());
                if (file.isDirectory() && file.getName() != null && file.getName().equals("hello")) {
                    destFile = file;
                }
            }
            // 进行文件复制
            if (destFile != null) {
                DocumentFile newFile = destFile.createFile("*/*", destFileName);
                if (newFile != null) {
                    return copyFile(context, inFile, newFile);
                } else {
                    Log.w(TAG, "newFile is null");
                }
            }
        }
        return false;
    }

    /**
     * ref: https://www.jianshu.com/p/2f5d80688ca6
     */
    private static boolean copyFile(Context context, File inFile, DocumentFile documentFile) {
        OutputStream out = null;
        try {
            InputStream in = new FileInputStream(inFile);
            out = context.getContentResolver().openOutputStream(documentFile.getUri());
            byte[] buf = new byte[1024 * 10];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            in.close();
            return true;
        } catch (IOException e) {
            Log.e(TAG, "error = " + e.getMessage());
            e.printStackTrace();
            return false;
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

MainActivity.java

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.support.annotation.Nullable;
import android.support.v4.provider.DocumentFile;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final int REQUEST_CODE = 1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        verifyStoragePermissions(this,REQUEST_CODE);
        String sdPath = StorageUtils.getSDCardDir(this);
        if (sdPath != null) {
            String str = DocumentsUtils.getTreeUri(this,sdPath);
            if (TextUtils.isEmpty(str)) {
                showOpenDocumentTree(sdPath);
            }
        }
    }

    private void showOpenDocumentTree(String rootPath) {
        Intent intent = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            StorageManager sm = getSystemService(StorageManager.class);
            StorageVolume volume = sm.getStorageVolume(new File(rootPath));
            if (volume != null) {
                intent = volume.createAccessIntent(null);
            }
        }
        if (intent == null) {
            intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
        }
        Log.d(TAG,"startActivityForResult...");
        startActivityForResult(intent, DocumentsUtils.OPEN_DOCUMENT_TREE_CODE);
    }

    public void onClick(View v) {
        String path = StorageUtils.getUsbDir(this);
        if (!TextUtils.isEmpty(path)) {
            File[] files = new File(path).listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.getName().endsWith(".zip")) {
                        Log.d(TAG, "file path=" + file.getAbsolutePath());
                    }
                }
            }
        } else {
            Log.d(TAG, "没有插入U盘");
        }
    }

    public void onClick1(View v) {
        String rootPath = StorageUtils.getSDCardDir(this);
        String urlStr = DocumentsUtils.getTreeUri(this, rootPath);
        Uri uri = Uri.parse(urlStr);
        writeFile(this, uri);
        DocumentsUtils.doCopyFile(this,null,"/storage/emulated/0/helloWorld.apk","copyApk.apk");
    }

    /**
     * 在外置TF卡根目录下创建目录
     */
    public void writeFile(Context context, Uri uri) {
        if (uri == null) {
            String sdPath = StorageUtils.getSDCardDir(context);
            if (TextUtils.isEmpty(sdPath)) {
                Log.w(TAG, "init folder is null path");
                return;
            }
            String uriStr = DocumentsUtils.getTreeUri(context, sdPath);
            if (TextUtils.isEmpty(uriStr)) {
                return;
            }
            uri = Uri.parse(uriStr);
        }
        DocumentFile documentFile = DocumentFile.fromTreeUri(context, uri);
        // 创建文件,并写入字符串
        if (!DocumentsUtils.isExist(documentFile, "hello")) {
            if (documentFile != null) {
                try {
                    DocumentFile fileHello = documentFile.createDirectory("hello");
                    DocumentFile fileText = fileHello.createFile("text/plain", "test.txt");
                    OutputStream out = null;
                    try {
                        out = context.getContentResolver().openOutputStream(fileText.getUri());
                        out.write("A long time ago...".getBytes());
                        out.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    DocumentFile fileImage = fileHello.createFile("image/png", "luncher.png");
                    try {
                        out = context.getContentResolver().openOutputStream(fileImage.getUri());
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher).compress(Bitmap.CompressFormat.JPEG, 100, baos);
                        InputStream inputimage = new ByteArrayInputStream(baos.toByteArray());
                        byte[] bytes = new byte[1024 * 10];
                        int len = 0;
                        while ((len = inputimage.read(bytes)) != -1) {
                            out.write(bytes, 0, len);
                        }
                        out.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case DocumentsUtils.OPEN_DOCUMENT_TREE_CODE:
                if (data != null && data.getData() != null) {
                    Uri uri = data.getData();
                    String rootPath = StorageUtils.getSDCardDir(this);
                    DocumentsUtils.saveTreeUri(this, rootPath, uri);
                    final int takeFlags = data.getFlags()
                            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                    Log.d(TAG, "save tree uri = " + uri);
                    // 相当于将url存储下来,下次开启apk不在进行弹框权限请求
                    getContentResolver().takePersistableUriPermission(uri, takeFlags);
                }
                break;
        }
    }

    private boolean verifyStoragePermissions(Activity activity, int requestCode) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

            int write = activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
            int read = activity.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
            //检测是否有权限,如果没有权限,就需要申请
            if (write != PackageManager.PERMISSION_GRANTED ||
                    read != PackageManager.PERMISSION_GRANTED) {
                //申请权限
                activity.requestPermissions(new String[]{
                        Manifest.permission.READ_EXTERNAL_STORAGE,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE
                }, requestCode);
                return false;
            }
        }
        return true;
    }
}

参考:

https://blog.csdn.net/qq_27531813/article/details/90369993

https://blog.csdn.net/weixin_33734979/article/details/84582017

 

 

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值