有些东西很简单,简单到你不想去想,比如:为什么天是蓝的?--局限物语
零、前言
说一下本篇的初衷: coder盘作为工作盘有点乱,想整理一下
也想寻求一个方便管理工程的点子,既然File类玩的滚瓜烂熟,何妨玩一下
加之File的天然递归性,再熟悉一下递归也不错,所以便有此篇,本文前奏有点长,好戏在后面----注意:本文的重点不是操作,而是思想
一、创建Filer类
java的File对象只是提供一些基本信息,这也是情理之中
感觉可以封装一下,提供更多的信息,比如下面的,文件夹大小,子文件个数等
1.先举一个简单的例子:目录结构如下:
|---edite
|---top
|---toly
|---界面编程GUI
|---Edit.java
|---FileHelper.java
|---IOUtils.java
|---edit.jar
|---main.txt
复制代码
2.Filer信息封装
先实现子文件夹、文件的个数、文件夹大小三个属性
public class Filer {
private String name;//文件夹名
private int dirCount;//子文件夹数量---不包括自身
private int fileCount;//文件的个数
private long length; //文件夹大小
public File getFile() {
return file;
}
public int getDirCount() {
return dirCount - 1;
}
public int getFileCount() {
return fileCount;
}
public long getLength() {
return length;
}
}
复制代码
3.定义文件节点类
考虑到一个文件夹下或有多个文件,这里用ArrayList装一下,
考虑到默认情况下ArrayList的初始数组为10个,这里取4个(很可能一个文件夹里就一两个文件)
/**
* 文件节点
*/
private class FileNode {
public ArrayList<FileNode> child;//子节点集合
public File file; //文件路径
public FileNode(File file) {
this.file = file;
child = new ArrayList<>(4);
}
}
复制代码
4.Filer的初始化
private FileNode root;//根节点
public Filer(String rootPath) {
file = new File(rootPath);
root = new FileNode(file);//初始化根节点
}
复制代码
5.文件的扫描
你可以结合下面的分析,自己debug走一走
public Filer(String rootPath) {
file = new File(rootPath);
root = new FileNode(file);
scan(root);//扫描根目录
}
private void scan(FileNode node) {
File file = node.file;
if (file.isFile()) {//如果节点是文件
return;
}
File[] files = file.listFiles();
for (File f : files) {
FileNode child = new FileNode(f);
node.child.add(child);
if (f.isDirectory()) {
scan(child);
}
}
}
复制代码
第一次调用scan时,如果是文件夹,就遍历文件夹, 里面不管是文件还是文件夹都加到child里
当遇到文件夹是便会触发scan来扫描该文件夹,这便是最简单的递归
无返回值,只是触发行为(实际每次触发scan方法调用完成,会有方法出栈的步骤)
就这样一个树形的结构就形成了
6.既然一个一个节点连接了这颗树
那么完全可以在扫描的时候多做一些事,比如维护那三个成员变量
private void scan(FileNode node) {
File file = node.file;
if (file.isFile()) {//如果节点是文件
return;
}
File[] files = file.listFiles();
for (File f : files) {
FileNode child = new FileNode(f);
node.child.add(child);
if (f.isDirectory()) {
dirCount++;//每调用一次说明有一个文件夹
scan(child);
} else {
fileCount++;//每调用一次说明有一个文件
length += f.length();//维护length
}
}
}
复制代码
7.调用
public class MakeDirInfo {
public static void main(String[] args) {
File root = new File("J:\\edite");
Filer filer = new Filer(root.getAbsolutePath());
long length = filer.getLength();//17.86621KB
System.out.println(Formater.format_B_KB_MB_GB(length));//5
long modifyTime = filer.getFile().lastModified();//2018-10-06 15:19:39
System.out.println(Formater.format_yyyy_MM_dd_kk_mm_ss(modifyTime));//5
System.out.println(filer.getFile().getName());//edite
System.out.println(+filer.getDirCount()+"个文件夹");//3
System.out.println(filer.getFileCount()+"个文夹");//5
System.out.println(filer.getFileFilter().get(0).count);//5
}
}
复制代码
用一个大一些的文件夹看一下:感觉7秒多挺慢的,电脑自身的查看器反应完要9秒,所以还好吧
二、全副武装
1.统计文件夹中文件类型
想一下,花了7秒得到三个属性,而且把文件都遍历一边了,感觉有点亏
现在想看一下有多少种文件,这要求不过分吧,想想也简单,看一下后缀名就行了
public Filer(String rootPath) {
mSet = new HashSet<>();
...
}
private void scan(FileNode node) {
File file = node.file;
if (file.isFile()) {//如果节点是文件
return;
}
File[] files = file.listFiles();
for (File f : files) {
FileNode child = new FileNode(f);
node.child.add(child);
if (f.isDirectory()) {
dirCount++;//每调用一次说明有一个文件夹
scan(child);
} else {
fileCount++;//每调用一次说明有一个文件
String fileName = f.getName();
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
mSet.add(suffix);
length += f.length();
}
}
}
复制代码
2.策略的出现
这有个问题,也许最近设计模式想多了,六大原则时刻在心
Filer在已经很好的完成了它的扫描工作,这里让Filer多一个成员变量mSet
感觉不爽,else里的三句代码看着也不优雅,如果需要改动,还有找在哪里,
代码如果多起来,茫茫码海,哪去找这三行!何不提取策略呢?
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:13:44
* 邮箱:1981462002@qq.com
* 说明:后缀名过滤器
*/
public class SuffixFilter {
private Set<String> suffixs;
public Set<String> getSuffixs() {
return suffixs;
}
public SuffixFilter() {
suffixs = new HashSet<>();
}
public void filter(File file) {
String fileName = file.getName();
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
suffixs.add(suffix);
}
}
---->[Filer]-------------
private SuffixFilter mFilter;
public Filer(SuffixFilter filter) {
mFilter = filter;
}
public void setFilter(SuffixFilter filter) {
mFilter = filter;
}
public void scan() {
scan(root);
}
public void scan() {
...
else {
fileCount++;//每调用一次说明有一个文件
if (mFilter != null) {
mFilter.filter(f);
}
length += f.length();
}
---->[使用]-------------
Filer filer = new Filer("J:\\edite");
SuffixFilter filter = new SuffixFilter();
filer.setFilter(filter);//设置过滤器
filer.scan();
Set<String> suffixs = filter.getSuffixs();
for (String suffix : suffixs) {
System.out.print(suffix+"、");
}
复制代码
3.当需要修改时,分离的优势显现
这样就是得Filer类和获取文件类型这个动作解耦,Filter只需要关注扫描任务
比如有些文件名没有后缀名,这样时就要修改策略,总不能都算一种文件吧?
有专门负责的类,只需去修改SuffixFilter的过滤方法就行了,比在Filer里好些
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:13:44
* 邮箱:1981462002@qq.com
* 说明:后缀名过滤器
*/
public class SuffixFilter {
private Set<String> suffixs;
public Set<String> getSuffixs() {
return suffixs;
}
public SuffixFilter() {
suffixs = new HashSet<>();
}
public void filter(File file) {
String fileName = file.getName();
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
if (suffix.length() < 10) {//如果长度大于10,是other
suffixs.add(suffix);
}else {
suffixs.add("other");
}
}
}
复制代码
4.抽象与拓展
既然是策略,就可以有多种,所以抽象出共性,自定义个性来拓展
很明显,有一个公共的抽象方法,filter(File),既然是过滤,应该有过滤条件
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:14:31
* 邮箱:1981462002@qq.com
* 说明:文件过滤接口
*/
public interface FileFilter {
/**
* 根据路径判断是否过滤出
* @param file 文件
* @return 是否可以执行filter
*/
boolean iCanGo(File file);
/**
* 过滤的逻辑操作
* @param file 文件
*/
void filter(File file);
}
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:13:44
* 邮箱:1981462002@qq.com
* 说明:后缀名过滤器
*/
public class SuffixFilter implements FileFilter{
private Set<String> suffixs;
public Set<String> getSuffixs() {
return suffixs;
}
public SuffixFilter() {
suffixs = new HashSet<>();
}
@Override
public boolean iCanGo(File file) {
return file.isFile();
}
public void filter(File file) {
String fileName = file.getName();
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
if (suffix.length() < 10) {//如果长度大于10,是other
suffixs.add(suffix);
}else {
suffixs.add("other");
}
}
}
---->[Filer]----------
private FileFilter mFilter;
public Filer(FileFilter filter) {
mFilter = filter;
}
public void setFilter(FileFilter filter) {
mFilter = filter;
}
public void scan() {
...
for (File f : files) {
FileNode child = new FileNode(f);
child.deep = curDeep;
node.child.add(child);
if (mFilter != null && mFilter.iCanGo(f)) {
mFilter.filter(f);
}
...
}
复制代码
5.基于接口的功能拓展
好好的为什么要加个接口,是我初学java时的一个大疑问,导致我设计模式稀里糊涂
现在要添加一个获取一个文件夹下的所有java文件的功能,有了接口就非常方便了
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:10:59
* 邮箱:1981462002@qq.com
* 说明:获取一个文件夹下所有java文件路径
*/
public class JavaFilter implements FileFilter {
private ArrayList<String> javaFiles;
public ArrayList<String> getJavaFiles() {
return javaFiles;
}
public JavaFilter() {
javaFiles = new ArrayList<>();
}
@Override
public boolean iCanGo(File file) {
String path = file.getAbsolutePath();
String suffix = path.substring(path.lastIndexOf(".") + 1);
return suffix.equals("java");
}
@Override
public void filter(File file) {
javaFiles.add(file.getAbsolutePath());
}
}
---->[使用]----------
Filer filer = new Filer("J:\\Github");
JavaFilter javaFilter = new JavaFilter();
filer.setFilter(javaFilter);
filer.scan();
for (String s : javaFilter.getJavaFiles()) {
System.out.println(s);
}
复制代码
可见过滤操作已经和Filer分离了,拓展了一个查看所有java文件的功能
没有修改Filer里的任何代码,对于Filer来说就是优秀的
iCanGo方法用来控制筛选,filter用来操作...一个控制系法师,一个输出系战士,吊打无误
当然你也可以将类型变成参数,通过构造来,这样会更方便
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:10:59
* 邮箱:1981462002@qq.com
* 说明:获取一个文件夹下所有某类型文件的路径
*/
public class TypeFilter implements FileFilter {
private ArrayList<String> javaFiles;
private String type;
public TypeFilter(String type) {
this.type = type;
javaFiles = new ArrayList<>();
}
public ArrayList<String> getJavaFiles() {
return javaFiles;
}
@Override
public boolean iCanGo(File file) {
String path = file.getAbsolutePath();
String suffix = path.substring(path.lastIndexOf(".") + 1);
return suffix.equals(type);
}
@Override
public void filter(File file) {
javaFiles.add(file.getAbsolutePath());
}
}
复制代码
6.批量修改操作
有很多时候感觉很像接力赛,一旦将棒子交给下一个人,你就不用跑了
Filer就是这样,我把遍历到的File交给你Filter,你爱怎搞自己搞,跟我无关
有点像服务器发数据,数据给你了我就没事了,你怎么处理你自己看着办
现在我想要将一个文件夹下的所有java文件结尾加一行字
的需求:
怎么操作我说了算,甚至不用新建类,写个匿名内部类就行了,拿到文件,就写呗!
Filer filer = new Filer("J:\\edite");
filer.setFilter(new FileFilter() {
@Override
public boolean iCanGo(String path) {
String path = file.getAbsolutePath();
String suffix = path.substring(path.lastIndexOf(".") + 1);
return suffix.equals("java");
}
@Override
public void filter(File file) {
try {
FileWriter fw = new FileWriter(file, true);
String time = Formater.format_yyyy_MM_dd_kk_mm_ss(System.currentTimeMillis());
fw.write("// 张风捷特烈 修改:" + time);
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
filer.scan();
复制代码
这样来看,批量改名字还不是手到擒来
7.多个过滤器
反正遍历都遍历了,不用白不用,多几个过滤器,多几重操作
//过滤器集合
private ArrayList<FileFilter> mFilters = new ArrayList<>();
public void addFilter(FileFilter countFilter) {
mFilters.add(countFilter);
}
public void scan() {
...
for (FileFilter filter : mFilters) {
if (filter != null && filter.iCanGo(f)) {
filter.filter(f);
}
}
...
---->[使用]-----
Filer filer = new Filer("J:\\edite");
JavaEditer javaEditer = new JavaEditer();
TypeFilter typeFilter = new TypeFilter("java");
filer.addFilter(javaEditer);
filer.addFilter(typeFilter);
filer.scan();
for (String s : typeFilter.getFiles()) {
System.out.println(s);
}
复制代码
三、言归正传
1.添加节点深度字段并维护
每当文件夹时curDeep++,跳出一次scan方法时curDeep--
public class Filer {
...
int curDeep;//节点深度
---->[Filer#scan]---------------
private void scan(FileNode node) {
File file = node.file;
if (file.isFile()) {//如果节点是文件
return;
}
File[] files = file.listFiles();
for (File f : files) {
FileNode child = new FileNode(f);
child.deep = curDeep;
node.child.add(child);
System.out.println(child.file.getAbsolutePath() + "--" + child.deep);
if (f.isDirectory()) {
dirCount++;//每调用一次说明有一个文件夹
curDeep++;
scan(child);
} else {
fileCount++;//每调用一次说明有一个文件
for (FileFilter filter : mFilters) {
if (filter != null && filter.iCanGo(f.getAbsolutePath())) {
filter.filter(f);
}
}
length += f.length();
}
}
curDeep--;
}
/**
* 文件节点
*/
private class FileNode {
...
public int deep;//深度
}
复制代码
2.修改接口
现在想把deep这个参数传出去...有两个方法,一种:修改接口!!!
第二种:新建接口,还好实现类不是很多,这里改一下接口吧...
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:14:31
* 邮箱:1981462002@qq.com
* 说明:文件过滤接口
*/
public interface FileFilter {
/**
* 根据路径判断是否过滤出
* @param path 路径
* @return 是否可以执行filter
*/
boolean iCanGo(File path);
/**
* 过滤的逻辑操作
* @param file 文件
* @param deep 该文件深度
*/
void filter(File file, int deep);
}
---->[Filer#scan]---------------------------
private void scan(FileNode node) {
...
for (File f : files) {
FileNode child = new FileNode(f);
child.deep = curDeep;
node.child.add(child);
for (FileFilter filter : mFilters) {
if (filter != null && filter.iCanGo(f)) {
filter.filter(child.file, child.deep);//回调出去深度
}
}
...
}
复制代码
3.打印目录结构
一直觉得目录结构挺帅气,今天总算自己弄出来了,可喜可贺,可喜可贺
-----------------创建目录结构过滤器----------------
|--这里iCanGo返回true,也就是畅通无阻,相当于一个监听器
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:10:59
* 邮箱:1981462002@qq.com
* 说明:创建目录结构
*/
public class StructureBuilder implements FileFilter {
String prefix;
String blank;
StringBuilder sb = new StringBuilder("目录结构:\n");
public String getStructure() {
return sb.toString();
}
public StructureBuilder() {
this("|---", " ");
}
public StructureBuilder(String prefix, String blank) {
this.prefix = prefix;
this.blank = blank;
}
@Override
public boolean iCanGo(File file) {
return true;
}
@Override
public void filter(File file, int deep) {
sb
.append(blankBuilder(blank, deep))
.append(prefix)
.append(file.getName())
.append("\n");
}
private static StringBuilder blankBuilder(String symbol, int num) {
StringBuilder stringBuffer = new StringBuilder();
for (int i = 0; i < num; i++) {
stringBuffer.append(symbol);
}
return stringBuffer;
}
}
---->[使用]---------
Filer filer = new Filer("J:\\C++");
StructureBuilder structureBuilder = new StructureBuilder();
filer.addFilter(structureBuilder);
filer.scan();
System.out.println(structureBuilder.getStructure());
复制代码
这样,样式随便你来定义:
new StructureBuilder("|---","····")
4.保存文件目录树
在根目录下保存一个文件目录树,这样方便查看,也让你知道大概这个文件夹怎么样
有多少文件,字符串在这里,你可以随意分析,玩弄,是不是很有趣
---->[StructureBuilder#writeFile]---------------------------
/**
* 将文件结构写入文件
* @param file 文件
*/
public void writeFile(File file) {
FileWriter fw = null;
try {
fw = new FileWriter(file);
String time = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.CHINA)
.format(System.currentTimeMillis());
fw.write("张风捷特烈--修改于" + time + "\n");
fw.write(sb.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
复制代码
看这个目录里有11W个文件,好吧,吓我一跳,生成也只用了7秒多
5.创建json格式的文件描述文件夹
没想到用个节点之后这么方便,Gosn秒出结构
依赖:implementation 'com.google.code.gson:gson:2.8.5'
好了,Json在手,去解析着玩吧
Filer filer = new Filer("J:\\edite");
filer.scan();
String json = new GsonBuilder().setPrettyPrinting().create().toJson(filer);
System.out.println(json);
复制代码
{
"file": {
"path": "J:\\edite"
},
"dirCount": 4,
"fileCount": 6,
"length": 11890,
"curDeep": -1,
"root": {
"child": [
{
"child": [],
"file": {
"path": "J:\\edite\\edit.jar"
},
"deep": 0
},
{
"child": [],
"file": {
"path": "J:\\edite\\main.txt"
},
"deep": 0
},
{
"child": [],
"file": {
"path": "J:\\edite\\structure.txt"
},
"deep": 0
},
{
"child": [
{
"child": [
{
"child": [
{
"child": [],
"file": {
"path": "J:\\edite\\top\\toly\\界面编程GUI\\Edit.java"
},
"deep": 3
},
{
"child": [],
"file": {
"path": "J:\\edite\\top\\toly\\界面编程GUI\\FileHelper.java"
},
"deep": 3
},
{
"child": [],
"file": {
"path": "J:\\edite\\top\\toly\\界面编程GUI\\IOUtils.java"
},
"deep": 3
}
],
"file": {
"path": "J:\\edite\\top\\toly\\界面编程GUI"
},
"deep": 2
}
],
"file": {
"path": "J:\\edite\\top\\toly"
},
"deep": 1
}
],
"file": {
"path": "J:\\edite\\top"
},
"deep": 0
}
],
"file": {
"path": "J:\\edite"
},
"deep": 0
}
}
复制代码
6.还是自己优化一下json吧
上面的json看着不爽,把root字段屏蔽掉,看一下本项目目录信息吧
保存到文件夹里也是一样的,这里就不演示了
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:10:59
* 邮箱:1981462002@qq.com
* 说明:创建目录JSON结构
*/
public class JsonDirBuilder implements FileFilter {
List<Filer> dirs;
List<Filer> files;
public String getStructure() {
return new GsonBuilder().setPrettyPrinting().create().toJson(dirs);
}
public JsonDirBuilder() {
dirs = new ArrayList<>();
files = new ArrayList<>();
}
@Override
public boolean iCanGo(File file) {
return true;
}
@Override
public void filter(File file, int deep) {
Filer filer = new Filer(file.getAbsolutePath());
filer.scan();
filer.curDeep = deep;
if (file.isDirectory()) {
dirs.add(filer);
} else {
files.add(filer);
}
}
/**
* 将文件结构写入文件
*
* @param file 文件
*/
public void writeFile(File file) {
FileWriter fw = null;
try {
fw = new FileWriter(file);
String time = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.CHINA)
.format(System.currentTimeMillis());
fw.write("张风捷特烈--修改于" + time + "\n");
fw.write(getStructure());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
复制代码
[
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\out"
},
"dirCount": 7,
"fileCount": 12,
"length": 22907,
"curDeep": 0
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\out\\production"
},
"dirCount": 6,
"fileCount": 12,
"length": 22907,
"curDeep": 1
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\out\\production\\classes"
},
"dirCount": 5,
"fileCount": 12,
"length": 22907,
"curDeep": 2
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\out\\production\\classes\\app"
},
"dirCount": 1,
"fileCount": 4,
"length": 9499,
"curDeep": 3
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\out\\production\\classes\\bean"
},
"dirCount": 1,
"fileCount": 1,
"length": 2259,
"curDeep": 3
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\out\\production\\classes\\filter"
},
"dirCount": 1,
"fileCount": 6,
"length": 9800,
"curDeep": 3
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\out\\production\\classes\\utils"
},
"dirCount": 1,
"fileCount": 1,
"length": 1349,
"curDeep": 3
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src"
},
"dirCount": 11,
"fileCount": 11,
"length": 18285,
"curDeep": 0
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src\\main"
},
"dirCount": 7,
"fileCount": 11,
"length": 18285,
"curDeep": 1
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src\\main\\java"
},
"dirCount": 5,
"fileCount": 11,
"length": 18285,
"curDeep": 2
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src\\main\\java\\app"
},
"dirCount": 1,
"fileCount": 3,
"length": 7919,
"curDeep": 3
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src\\main\\java\\bean"
},
"dirCount": 1,
"fileCount": 1,
"length": 1935,
"curDeep": 3
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src\\main\\java\\filter"
},
"dirCount": 1,
"fileCount": 6,
"length": 7477,
"curDeep": 3
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src\\main\\java\\utils"
},
"dirCount": 1,
"fileCount": 1,
"length": 954,
"curDeep": 3
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src\\main\\resources"
},
"dirCount": 1,
"fileCount": 0,
"length": 0,
"curDeep": 2
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src\\test"
},
"dirCount": 3,
"fileCount": 0,
"length": 0,
"curDeep": 1
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src\\test\\java"
},
"dirCount": 1,
"fileCount": 0,
"length": 0,
"curDeep": 2
},
{
"file": {
"path": "J:\\FileUnit\\file_java\\file\\src\\test\\resources"
},
"dirCount": 1,
"fileCount": 0,
"length": 0,
"curDeep": 2
}
]
复制代码
7.点睛之笔
写了老半天,现在把写的东西考到
build.gradle
里,当插件用
Groovy完全兼容java,所以,都考进去就行了,上面的所以都可以当脚本用
Gradle真是挺好玩的,以后有什么想偷懒的,写个java版的,在一贴,就能当脚本用
今天东西有点杂,好好消化消化吧,感觉发现了新天地
--------------------------插件-------------------------------------------
apply plugin: ListDirPlugin//声明使用插件
listDir {//根据拓展参数来自定义文件夹
path = 'J:\\FileUnit\\file_java\\file'
}
--------------------------插件书写-------------------------------------------
public class Filer {
private File file;//文件夹名
private int dirCount = 1;//子文件夹数量---不包括自身
private int fileCount;//文件的个数
private long length; //文件夹大小
//过滤器集合
private transient ArrayList<FileFilter> mFilters = new ArrayList<>();
public int curDeep;//节点深度
public void addFilter(FileFilter countFilter) {
mFilters.add(countFilter);
}
public File getFile() {
return file;
}
public int getDirCount() {
return dirCount;
}
public int getFileCount() {
return fileCount;
}
public long getLength() {
return length;
}
private transient FileNode root;//根节点
public Filer(String rootPath) {
file = new File(rootPath);
root = new FileNode(file);
}
public void scan() {
scan(root);
}
public void scan(FileNode node) {
File file = node.file;
if (file.isFile()) {//如果节点是文件
return;
}
File[] files = file.listFiles();
for (File f : files) {
FileNode child = new FileNode(f);
child.deep = curDeep;
node.child.add(child);
for (FileFilter filter : mFilters) {
if (filter != null && filter.iCanGo(f)) {
filter.filter(child.file, child.deep);
}
}
if (f.isDirectory()) {
dirCount++;//每调用一次说明有一个文件夹
curDeep++;
scan(child);
} else {
fileCount++;//每调用一次说明有一个文件
length += f.length();
}
}
curDeep--;
}
/**
* 将Filer的数据进行初始化
* 遍历当前节点
*
* @param target
* @return
*/
private long traverse(FileNode target) {//--------瞎猫碰到死耗子
if (target.child == null) {
return length;
}
if (target.file.isDirectory()) {
dirCount++;//每调用一次说明有一个文件夹
for (FileNode node : target.child) {
length += traverse(node);
}
return length;
} else {
fileCount++;//每调用一次说明有一个文件
return length + target.file.length();
}
}
/**
* 文件节点
*/
private class FileNode {
public ArrayList<FileNode> child;//子节点集合
public File file; //文件路径
public int deep;//深度
public FileNode(File file) {
this.file = file;
child = new ArrayList<>(4);
}
}
@Override
public String toString() {
return "Filer{" +
"file=" + file +
", dirCount=" + dirCount +
", fileCount=" + fileCount +
", length=" + length +
", curDeep=" + curDeep +
'}';
}
}
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:14:31
* 邮箱:1981462002@qq.com
* 说明:文件过滤接口
*/
public interface FileFilter {
/**
* 根据路径判断是否过滤出
* @param path 路径
* @return 是否可以执行filter
*/
boolean iCanGo(File path);
/**
* 过滤的逻辑操作
* @param file 文件
* @param deep 该文件深度
*/
void filter(File file, int deep);
}
/**
* 作者:张风捷特烈
* 时间:2019/2/13/013:10:59
* 邮箱:1981462002@qq.com
* 说明:创建目录结构
*/
public class StructureBuilder implements FileFilter {
String prefix;
String blank;
StringBuilder sb = new StringBuilder("目录结构:\n");
public String getStructure() {
return sb.toString();
}
public StructureBuilder() {
this("|---", "····");
}
public StructureBuilder(String prefix, String blank) {
this.prefix = prefix;
this.blank = blank;
}
@Override
public boolean iCanGo(File file) {
return true;
}
@Override
public void filter(File file, int deep) {
sb
.append(blankBuilder(blank, deep))
.append(prefix)
.append(file.getName())
.append("\n");
}
private static StringBuilder blankBuilder(String symbol, int num) {
StringBuilder stringBuffer = new StringBuilder();
for (int i = 0; i < num; i++) {
stringBuffer.append(symbol);
}
return stringBuffer;
}
/**
* 将文件结构写入文件
* @param file 文件
*/
public void writeFile(File file) {
FileWriter fw = null;
try {
fw = new FileWriter(file);
String time = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.CHINA)
.format(System.currentTimeMillis());
fw.write("张风捷特烈--修改于" + time + "\n");
fw.write(sb.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//----------------------------以下是插件部分--------------------------------
class ListDirPlugin implements Plugin<Project> {
//该接口定义了一个apply()方法,在该方法中,我们可以操作Project,
//比如向其中加入Task,定义额外的Property等。
void apply(Project project) {
//加载Extension
project.extensions.create("listDir", MkDirPluginPluginExtension)
//使用Extension配置信息
project.task('listDirTask') << {
String path = project.listDir.path
Filer filer = new Filer(path);
StructureBuilder structureBuilder = new StructureBuilder("|---","....");
filer.addFilter(structureBuilder);
filer.scan();
System.out.println(structureBuilder.getStructure());
File file = new File(filer.getFile(), "structure.txt");
structureBuilder.writeFile(file);
}
}
}
class MkDirPluginPluginExtension {//拓展参数
String path = ''
}
复制代码
后记:捷文规范
1.本文成长记录及勘误表
项目源码 | 日期 | 附录 |
---|---|---|
V0.1-- | 2018-2-13 | 无 |
发布名:
杂篇-从文件操作来发起的杂谈[-File-]
捷文链接:www.jianshu.com/u/e4e52c116…
2.更多关于我
笔名 | 微信 | |
---|---|---|
张风捷特烈 | 1981462002 | zdl1994328 |
我的github:github.com/toly1994328
我的简书:www.jianshu.com/u/e4e52c116…
我的掘金:juejin.im/user/5b42c0…
个人网站:www.toly1994.com
3.声明
1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持