1. VFS - 代码生成器预览功能实现
  2. VFS - 虚拟文件系统基本操作方法的封装
  3. VFS - 虚拟文件系统的加载和导出

接前一篇 VFS - 代码生成器预览功能实现 ,上一篇讲到了 mkdirs 封装创建目录的方法,接下来先处理前文中的BUG,然后再封装文件的基础方法。

文件的 BUG

在前一篇文章中,认为一个文件的 nametype 同时决定了唯一的一个文件,这个设计没有问题,但是经过在不同操作系统测试发现,同一个文件名只能在一个目录中出现一次,名字决定了唯一的一个文件,类型决定了可以对这个文件进行什么样的操作。并且默认情况下只有 Linux 对文件名区分大小写,Window 和 macOS 默认都不区分大小写,因为文件名的类型为 Path,我特地看了看不同操作系统下 Path 的比较方式,JDK 两类系统的源码实现:

  • WindowsPath.java
  • UnixPath.java

Windows 的实现中最终比较 Path 时会转换为大写进行比较,Unix 实现不会进行转换。因为文件名使用的 Path 类型,所以直接比较 Path name 就支持了不同操作系统的不同实现。

由于 macOS 本身默认不区分大小写(和磁盘分区格式有关),但是 macOS 的 jdk 实现使用的 UnixPath,这就产生了一个 Java 的BUG:macOS 的代码上区分大小写,真正创建文件时又不区分大小写,会导致代码上多出的文件丢失

例如先创建一个 a.txt 文件,在创建一个 A.txt 文件时,代码中认为有两个文件,实际硬盘上只有一个 a.txt 文件,当两个文件依次写入内容时,第二个文件的内容会覆盖 a.txt 的内容。所以在真正写代码时,文件名不能区分大小写。

先修改下面两个基础方法:

@Override
public boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}VFSNode vfsNode = (VFSNode) o;return name.equals(vfsNode.name);
}@Override
public int hashCode() {return Objects.hash(name);
}

由于只通过文件名判断文件是否已经存在,因此添加直接的子文件时增加判断已经存在的文件类型是否一致:

private void addChild(VFSNode child) {if (CollUtil.isEmpty(this.files)) {this.files = new ArrayList<>();}if (this.files.contains(child)) {VFSNode same = getChild(child.name);if (same.type != child.type) {throw new RuntimeException("已经存在类型为 "+ same.type + " 的文件,无法添加 " + child.type + " 类型");}} else {this.files.add(child);child.parent = this;}
}

最后一个改动的地方就是添加子文件过程中,需要判断中间的文件是否为目录,如果不是目录也不允许往下添加子文件。

protected void addVFSNode(VFSNode node, Path relativePath) {int nameCount = relativePath.getNameCount();if (nameCount > 1) {Path name = relativePath.getName(0);VFSNode vfsNode = getChild(name);if (vfsNode == null) {vfsNode = new VFSNode(name, Type.DIR);addChild(vfsNode);}//增加判断节点类型if(vfsNode.isDirectory()) {vfsNode.addVFSNode(node, relativePath.subpath(1, nameCount));} else {throw new RuntimeException("无法向文件 " + vfsNode.name + " 下添加子文件");}} else if (nameCount == 1) {addChild(node);}
}

经过上面的修改后,VFS中的文件名和类型的规则和操作系统就一致了,为后面和操作系统上的文件系统交互打下了基础。

VFS 文件基本操作

VFSNode 中通过简单的 writeread 方法实现了虚拟文件的读写,而且文件还支持多次覆盖写入,并且记录文件写入内容的历史,在外层 VFS 中封装时,仍然是先要通过相对路径找到要写入的文件,如果文件不存在还要先创建该文件。找到要写入的虚拟文件后,调用 VFSNode 的写入方法就可以实现。在 VFSNode 中也提过 找到要操作的文件是其他操作的基础,这里还是先封装该方法:

/*** 查找节点** @param relativePath 相对路径* @return*/
protected VFSNode findVFSNode(Path relativePath) {return findVFSNode(relativePath, null, false);
}/*** 查找指定类型节点** @param relativePath 相对路径* @param type         文件类型* @return*/
protected VFSNode findVFSNode(Path relativePath, Type type) {return findVFSNode(relativePath, type, false);
}/*** 查找节点** @param relativePath      相对路径* @param type              文件类型* @param createIfNotExists 如果不存在就创建,这种情况要么返回节点要么抛出异常* @return*/
protected VFSNode findVFSNode(Path relativePath, Type type, boolean createIfNotExists) {//检查相对路径是否合法(不能超出根路径范围)checkRelativePath(relativePath);//根据相对路径查找节点VFSNode node = getVFSNode(relativePath);//当节点存在、指定了类型、并且类型不一致时if (node != null && type != null && node.type != type) {//如果需要创建就抛出异常if (createIfNotExists) {throw new RuntimeException("已经存在类型为 " + node.type+ " 的文件,无法创建 " + type + "类型的同名文件");}//不需要创建就因为类型不一致返回nullreturn null;}//文件不存在并且需要创建时if (node == null && createIfNotExists) {//新建节点node = new VFSNode(relativePath.getFileName(), type);//根据相对路径添加节点addVFSNode(node, relativePath);}return node;
}

下面开始基于这个基础方法开始实现文件的基本操作,先看删除文件。

VFS 文件操作 - 删除

mkdirs 方法类似,有三种形式参数的方法,删除文件时,查找到对应的 VFSNode 调用对象上的 delete 方法断绝和父级的关系即可。

/*** 删除指定文件** @param file 文件* @return true 删除成功,false 文件不存在*/
public boolean delelte(File file) {return delelte(relativize(file));
}/*** 删除指定文件** @param relativePath 相对路径* @return true 删除成功,false 文件不存在*/
public boolean delelte(String relativePath) {return delelte(toPath(relativePath));
}/*** 删除相对路径的文件** @param relativePath 相对路径* @return true 删除成功,false 文件不存在*/
public boolean delelte(Path relativePath) {VFSNode vfsNode = findVFSNode(relativePath);if (vfsNode != null) {vfsNode.delete();return true;}return false;
}

删除非常简单,下面的写入文件也很简单。

VFS 文件操作 - 写文件

为了支持更多类型的文件,VFSNode 中的文件内容使用的 byte[] 字节数组类型,因此首先提供写入 bytes[] 的方法:

/*** 写入文件内容** @param file  文件* @param bytes 内容*/
public void write(File file, byte[] bytes) {write(relativize(file), bytes);
}/*** 写入文件内容** @param relativePath 相对路径* @param bytes        内容*/
public void write(String relativePath, byte[] bytes) {write(toPath(relativePath), bytes);
}/*** 写入相对文件内容** @param relativePath 相对路径* @param bytes        文件内容*/
public void write(Path relativePath, byte[] bytes) {//获取文件并写入数据,获取时指定为 FILE 类型,如果文件不存在就创建findVFSNode(relativePath, Type.FILE, true).write(bytes);
}

byte[] 类型的内容更通用,String 类型的文件比 byte[] 更常用。

/*** 写入文件内容** @param file    文件* @param content 内容*/
public void write(File file, String content) {write(relativize(file), content);
}/*** 写入文件内容** @param relativePath 相对路径* @param content      内容*/
public void write(String relativePath, String content) {write(toPath(relativePath), content);
}/*** 写入相对文件内容** @param relativePath 相对路径* @param content      文件内容*/
public void write(Path relativePath, String content) {findVFSNode(relativePath, Type.FILE, true).write(content.getBytes(StandardCharsets.UTF_8));
}

基本操作功能测试

VFS vfs = VFS.of("/");
vfs.mkdirs("/a");
vfs.mkdirs("/a/b");
vfs.mkdirs("/a/c");
vfs.mkdirs("/a/c");
vfs.mkdirs("/a/d/e.txt");
vfs.write("/a/help.txt", "帮助文档");
vfs.delelte("/a/c");
System.out.println(vfs.print());

输出的文件结构如下:

/
└── a├── b├── d│   └── e.txt└── help.txt

又是未完,待续…

又写了很长时间还没写完,一个是想表达的内容比较多,写的太长怕一次读不完,另外更主要的原因是:写文章的时候要换一个角度对代码进行重新理解并展示给读者,重新理解的过程也涉及到了代码的重构,因此写文章的同时也在写代码。

虽然未完,但是后续要写的整体内容基本上已经明确了,后续还有一篇,主要是加载指定的目录到VFS中,将VFS的内容写入到指定的目录中。除了目录外,为了便于使用专门增加了 ZIP 文件的支持,这次 ZIP 导入导出的功能让我又重新认识了 Java 的 ZIP,下一篇文章在详细介绍。

由于代码还在变,恐怕最后一篇文章写出前不适合再发源码,等最后再重新给所有在博客、微信留邮箱的各位读者朋友发送一遍代码。

VFS - 虚拟文件系统基本操作方法的封装相关推荐

  1. VFS - 虚拟文件系统的加载和导出

    VFS - 代码生成器预览功能实现 VFS - 虚拟文件系统基本操作方法的封装 VFS - 虚拟文件系统的加载和导出 这是 VFS 的最后一篇,前面两篇中的基本方法已经实现了一个简单的虚拟文件系统,可 ...

  2. Linux 字符设备驱动开发基础(六)—— VFS 虚拟文件系统解析

    一.VFS 虚拟文件系统基础概念 Linux 允许众多不同的文件系统共存,并支持跨文件系统的文件操作,这是因为有虚拟文件系统的存在.虚拟文件系统,即VFS(Virtual File System)是 ...

  3. Linux(一) VFS虚拟文件系统

    一.先了解一下什么是挂载 Linux有自己的一套文件系统,例如Ext2.Ext3,但是外部其他文件系统时,由于各个文件系统都各自有一套的文件管理体系,是无法通过Linux本身访问文件的方式直接访问的, ...

  4. linux VFS 虚拟文件系统 简介 super_block inode dentry file

    1.简介 1.Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等.通过使用同一套文件 I/O 系统 调用即可对 Linux 中的任意文件进行操作而无需考虑其所在的具体文 ...

  5. Linux·VFS虚拟文件系统

    目录 1 概念 2 架构 3 接口适配示例 4 跨设备/文件系统示例 5 VFS的抽象接口 6 Linux系统VFS支持的文件系统 7 统一文件模型(common file model) 7.1 Su ...

  6. 【文件系统】VFS虚拟文件系统

    1.VFS Linux支持各种各样的文件系统格式,比如说ext2,ext3,reiserfs,FAT,NTFS,iso9660等, 不同的磁盘分区,光盘或者其他存储设备都有不同的文件系统格式,然后这些 ...

  7. 11Linux服务器编程之:VFS虚拟文件系统,dup()函数和dup2()函数

     1dup函数和dup2函数 #include<unistd.h> int dup(intoldfd); int dup2(intoldfd, int newfd); dup和dup2 ...

  8. Linux操作系统~系统文件IO,什么是文件描述符fd?什么是vfs虚拟文件系统

    目录 1.open() (1).第二个参数flags-通过比特位传多组标记 2.文件描述符fd(open函数的返回值) (1).fd的本质 (2).vfs-虚拟文件系统(一切皆文件) (3).调用re ...

  9. 鸿蒙轻内核源码分析:虚拟文件系统 VFS

    本文分享自华为云社区<鸿蒙轻内核M核源码分析系列二一 01 虚拟文件系统VFS>,作者:zhushy . VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际 ...

最新文章

  1. 如何关闭Visual Assist?
  2. python安装包为什么这么小-为什么你的Python包老是装不上?收下这个网站就对了...
  3. 光模块的正确安装方法和使用须知
  4. 工作流实战_12_flowable 流程实例 终止流程
  5. java opencv安装路径_Java搭建opencv开发环境
  6. LeetCode Sort Colors
  7. 自定义Writable类型
  8. selenium与chromedriver的操作
  9. 2018年计算机职称考试冲刺,2018年中级会计职称考试冲刺阶段学习计划
  10. Trick(九)—— ++i 与 i++ 的本质区别
  11. kotlin多继承_Kotlin继承
  12. cplex java_线性最优解java实现+Cplex java调用
  13. Linux内核移植之DM9000网卡驱动
  14. python考勤统计_公司HR统计考勤用这个函数公式,快速、准确完成,再也不加班了...
  15. 百度2023校招 内推码IVV4AS
  16. 多轴机械人运动学正逆解,简单粗暴!!!!!!
  17. 家庭NAS方案-树莓派安装使用OpenMediaVault
  18. 『 文件操作 』文件批量改名
  19. 分解质因数 (10 分)
  20. 手机验证码平台,怎么发送手机验证码,php开发手机验证码短信接口功能

热门文章

  1. 中国大陆18位身份证校验算法(附JS实现)
  2. 深度学习acc曲线与loss曲线
  3. iOS 应用“无法安装应用程序 因为证书无效“的解决方案
  4. 无源蜂鸣器按特定频率发出相对应的音调
  5. 企业如何做好微信推广营销——类聚平台来指导
  6. 5月5日病毒播报:“小不点”与“游戏窃贼
  7. CPU进入IDLE都做了啥?
  8. 老年人腰疼的原因有哪些?4种方法治疗腰疼
  9. 小型商业用房火灾预防的措施
  10. java开发进度条怎么监控,上传文件时监控读取进度,显示进度条