AssetFile - 像File一样操作Asset资源

版权声明:欢迎关注我的Github:https://github.com/dengyuhan,转载请注明出处,欢迎转载 https://blog.csdn.net/aa464971/article/details/82740299

转载请注明出处:
https://blog.csdn.net/aa464971/article/details/82740299
*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

https://github.com/dengyuhan/AssetFile

如果想跳过直接看文档,可以拉到最底

前言

平时开发中经常会用到Assets,可以让我们把一些资源内置在应用里,但是它使用起来比较麻烦,比如要使用Assets里面的一个文件,需要这样:

try {
    final InputStream stream = getAssets().open("test.jpg");
} catch (IOException e) {
    e.printStackTrace();
}

拿到的是InputStream,当需要复制到手机外部存储的时候,还得用FileOutputStream输出到文件,如果需要获取文件夹下面的文件:

try {
    final String[] list = getAssets().list("test");
} catch (IOException e) {
    e.printStackTrace();
}

这样获取到的只是文件夹下的文件名,如果要复制整个文件夹到手机目录至少要3步:

  1. 遍历;
  2. 拼接完整路径;
  3. 调用open输出到文件。

强迫症表示很难受,没有Java File的API那么方便,那只能撸一个轮子让它像File一样了。
下面的图是用AssetFile做的Assets文件管理器

使用场景

先举一个我遇到的场景,在线滤镜和内置滤镜,在线滤镜需要先把配置文件下载到手机目录,内置滤镜就是放在Assets里的,为了方便管理,把两种滤镜都放到统一的目录,AssetFile先解决了一个复制文件夹的问题。

如果要获取下图的滤镜1.json,实际上是这样assetManager.open(filter/group/滤镜1/滤镜1.json),文件名和文件数量实际上是不受开发控制的,也不可能每次变化都再去修改一遍文件名或者代码,所以AssetFile将多层次文件夹的操作简单化了

AssetFile并没有省略那些流程,只是经过封装,让它使用起来变得更简单了

基本实现

首先先来实现一下基本功能,Java的叫File,那我们就叫AssetFile,先暂时存一些基本信息,路径和文件名

public class AssetFile {
    private String assetPath;
    private String name;
}

我们在用File的时候是传文件路径,AssetFile也一样,假设Assets根目录有一个test.jpg文件

public class AssetFile {
    private String assetPath;
    private String name;
    
    public AssetFile(String assetPath) {
        this.assetPath = assetPath == null ? "" : assetPath;
        //有/的话会去掉/
        int index = assetPath.lastIndexOf(File.separatorChar);
        this.name = assetPath.substring(index + 1, assetPath.length());
    }
}
new AssetFile("test.jpg")

文件API

基本信息也赋好值了,可以说已经有一个雏形了,现在添加一些常用的API,比如exists()
当调用AssetManager.list(assetPath)的时候,如果找不到这个文件,会抛出IOException,所以只要catch到就说明这个文件不存在。

public boolean exists(AssetManager assetManager) {
    try {
        assetManager.list(assetPath);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}
AssetFile file = new AssetFile("test.jpg")
boolean exists = file.exists(context.getAssets())

加入文件夹的支持

文件的API比较简单,要考虑文件夹就会有一些逻辑了

isDirectory()

现在我们加入一个isDirectory (),用来判断这个路径是否是文件夹,可以获取该路径下的子文件数组,来判断这个路径是不是一个文件夹,如果数组长度大于0说明是一个文件夹。

当然这个方法也有不准确的情况,比如你放了一个空文件夹在Assets里,但是为什么要放一个空文件夹在Assets里呢,所以这个方法还是可行的。

这里还有一个处理,assetManager.list()是把子文件的路径都添加到数组里了,所以不能每次都这样取一次子文件数组,可以用一个变量来缓存它,当它有值的时候,就直接用变量的值了。

public class AssetFile {
    private Boolean directory;

    public boolean isDirectory(AssetManager assetManager) {
        if (directory == null) {
            try {
                directory = assetManager.list(assetPath).length > 0;
            } catch (Exception e) {
                e.printStackTrace();
                directory = false;
            }
        }
        return directory;
    }
}
getParent()

还有一个常用的方法就是获取父文件夹,这个比较简单,通过分割路径字符串就可以实现。

比如A文件夹里放了B文件夹,B文件夹放了C文件,那路径其实就是A/B/C.jpg,通过substring0开始截取到最后的/,截取后的A/B就是父文件夹的路径了。

public String getParent() {
    int index = assetPath.lastIndexOf(File.separatorChar);
    return assetPath.substring(0, index);
}

public AssetFile getParentFile() {
    return new AssetFile(getParent());
}
listFiles()

既然有文件夹,就肯定会需要用到listFiles()assetManager.list()提供的只是子文件的文件名称,所以需要把它拼接成完整的路径传给AssetFile

还可以添加一个AssetFileFilterreturn false的就不添加到集合;了。当根目录的时候调用assetManager.list会把系统自带的一些文件列出来,所以这里还实现了一个SystemAssetFileFilter,用来过滤这些文件。

public List<AssetFile> listFiles(AssetManager assetManager) {
    return listFiles(assetManager, new SystemAssetFileFilter());
}

public List<AssetFile> listFiles(AssetManager assetManager, AssetFileFilter filter) {
    try {
        String newAssetPath = TextUtils.isEmpty(assetPath) ? "" : assetPath;
        //先获取子文件数组
        String[] list = assetManager.list(newAssetPath);
        List<AssetFile> fileList = new ArrayList<>();
        for (int i = 0; i < list.length; i++) {
            AssetFile file = new AssetFile(newAssetPath, list[i]);
            if (filter != null) {
        		//如果有过滤器,返回true的才添加到AssetFile集合
                if (filter.accept(file)) {
                    fileList.add(file);
                }
            } else {
            	//否则直接添加
                fileList.add(file);
            }
        }
        return fileList;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return new ArrayList<>();
}

AssetsManager

为了更方便的使用,可以再扩展一个AssetsManager,提供一些辅助性的方法,比如从Assets复制资源到手机目录,复制文件很简单,拿到流之后直接输出到文件,复制文件夹需要递归来达到复制多层文件夹的效果。

/**
 * 复制Asset文件夹和里面的文件到手机目录
 *
 * @param assetSource
 * @param outputDir
 */
public static boolean copyAsset(AssetManager assetManager, AssetFile assetSource, File outputDir) {
    try {
        File outputFile = new File(outputDir, assetSource.getName());

        String assetPath = assetSource.getAssetPath();
        final String[] list = assetManager.list(assetPath);
        if (list.length <= 0) {
            //文件
            copyAssetFile(assetManager, assetPath, outputFile);
        } else {
            //目录
            if (!outputFile.exists()) {
                outputFile.mkdirs();
            }
            for (String child : list) {
                copyAsset(assetManager, new AssetFile(assetPath, child), outputFile);
            }
        }
        return true;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return false;
}

/**
 * 复制Asset文件到手机目录
 *
 * @param assetPath
 * @param outputFile
 * @return
 */
public static boolean copyAssetFile(AssetManager assetManager, String assetPath, File outputFile) {
    try {
        InputStream is = assetManager.open(assetPath);
        int byteRead = 0;
        FileOutputStream fs = new FileOutputStream(outputFile);
        byte[] buffer = new byte[1024];
        while ((byteRead = is.read(buffer)) != -1) {
            fs.write(buffer, 0, byteRead);
        }
        fs.close();
        is.close();
        return true;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return false;
}

文档

Gradle引入
implementation 'com.dyhdyh.io:asset-file:1.0.2'
AssetFile
//根目录
AssetFile root = new AssetFile();
//根目录下的test.jpg
AssetFile testFile = new AssetFile("test.jpg");
//test文件夹下的test.jpg  - test/test.jpg
AssetFile testFile = new AssetFile("test/test.jpg");
AssetFile testFile = new AssetFile("test", "test.jpg");

//获取完整路径
assetFile.getAssetPath();

//获取文件名称或目录名称
assetFile.getName();

//获取父级目录
assetFile.getParentFile();

//转换Uri
assetFile.getUri();

//是否文件夹
assetFile.isDirectory(getAssets());

//是否根目录
assetFile.isRootDir();

//文件是否存在
assetFile.exists(getAssets());

//获取目录下的文件数组
assetFile.listFiles(getAssets());
AssetsManager
//复制Assets里的test.jpg到手机根目录
AssetFile assetFile = new AssetFile("test.jpg");
File outputFile = new File(Environment.getExternalStorageDirectory(), assetFile.getName());
AssetsManager.copyAssetFile(getAssets(), assetFile.getAssetPath(), outputFile);

//复制Assets里的test文件夹到手机根目录
AssetsManager.copyAssetFile(getAssets(), "test", Environment.getExternalStorageDirectory());
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页