虚拟文件系统VFS

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

Maven引用坐标:

?
1
2
3
4
5
< dependency >
< groupId >org.tinygroup</ groupId >
< artifactId >vfs</ artifactId >
< version >0.0.12</ version >
</ dependency >
一开始,本人抱着对Apache的绝对信任,选择了Apache VFS来进行文件访问的封装,确实,他的API是统一的、优雅的,支持的协议种类也非常多,在简单了解之后,觉得就用它吧,总不能什么轮子都自己造。

于是Apache VFS就进入了我的框架,功能也完全良好。但是在压力测试的时候,忽然发现有内存泄露问题,dump一下内存,进行分析之后,发现原来是Apache VFS 2.0惹得祸,看一看apache VFS已经好久没有升级了,跟踪了一下源码,发现有些地方,比较诡异,就是有时候进有时候不进,查之良久而不得,只好下决定把Apache VFS从里面拿掉,而拿掉之前,就需要有同样功能的东东支撑,不得已,花费2天时间写了一个VFS,功能比Apache的VFS少一些,但是够用了。然后花了一天时间,把原来Apache VFS的代码迁移到新的VFS之上,做此项迁移工作的小弟手指都木了,迁移完之后,再行测试,内存泄露问题不再存在。

好了,上面讲了前因,下面就介绍一下VFS的构成及实现:

模式提供者接口用于扩展各种文件来源,比如:本地文件,HTTP文件,FTP文件,Jar包文件,Zip文件等等,其接口声明如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
  * 模式提供者接口
  */
public interface SchemaProvider {
     /**
      * 是否匹配
      *
      * @param resource
      * @return 如果返回true,表示此提供者可以处理,返回false表示不能处理
      */
     boolean isMatch(String resource);
 
     /**
      * 返回处理的模式
      *
      * @return
      */
     String getSchema();
 
     /**
      * 解析资源,并返回文件对象
      *
      * @param resource
      * @return
      */
     FileObject resolver(String resource);
}

文件对象FileObject,是对文件的访问的统一接口,它的定义如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public interface FileObject {
     String getFileName(); // 返回文件名
 
     String getPath(); // 返回路径
 
     String getAbsolutePath(); // 返回绝对路径
 
     String getExtName(); // 返回扩展名
 
     boolean isExist(); // 是否存在
 
     long getSize(); // 返回文件大小
 
     InputStream getInputStream(); //返回输入流
 
     boolean isFolder(); // 返回是否是目录,如果是目录,则getInputStream无效。
 
     FileObject getParent(); // 返回上级文件
     
     void setParent(FileObject fileObject); // 设置上级文件
 
     List<FileObject> getChildren(); // 返回下级文件列表
     
     FileObject getChild(String fileName); //获取参数名称指定的fileobject
 
     long getLastModifiedTime(); // 返回修改时间
 
     SchemaProvider getSchemaProvider(); //返回模式提供者
 
     boolean isInPackage(); //是否是包文件
     
     URL getURL(); //返回url
     
     OutputStream getOutputStream(); //返回输出流
}
关键的接口就是上面两个,当然还有一个重要的类就是虚拟文件系统类,内容如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/**
  * 虚拟文件系统
  */
public class VFS {
  static Map<String, FileObject> fileObjectCacheMap = new ConcurrentHashMap<String, FileObject>();
  static Map<String, Long> fileModifyTimeMap = new ConcurrentHashMap<String, Long>();
  static final Map<String, SchemaProvider> schemaProviderMap = new HashMap<String, SchemaProvider>();
  private static String defaultSchema = "file:" ;
  static {
  addSchemaProvider( new JarSchemaProvider());
  addSchemaProvider( new ZipSchemaProvider());
  addSchemaProvider( new FileSchemaProvider());
  addSchemaProvider( new HttpSchemaProvider());
  addSchemaProvider( new HttpsSchemaProvider());
  addSchemaProvider( new FtpSchemaProvider());
  addSchemaProvider( new JBossVfsSchemaProvider());
  }
 
 
  /**
  * 构建函数私有化
  */
  private VFS() {
 
  }
  /**
  * 清空Cache
  */
  public static final void clearCache() {
  fileObjectCacheMap.clear();
  }
 
 
  /**
  * 添加新的模式提供者
 
  * @param schemaProvider
  */
  public static final void addSchemaProvider(SchemaProvider schemaProvider) {
  schemaProviderMap.put(schemaProvider.getSchema(), schemaProvider);
  }
  /**
  * 设置默认模式提供者
 
  * @param schema
  */
  public static final void setDefaultSchemaProvider(String schema) {
  defaultSchema = schema;
  }
  /**
  * 返回指定的模式提供者
 
  * @param schema
  * @return
  */
  public static final SchemaProvider getSchemaProvider(String schema) {
  return schemaProviderMap.get(schema);
  }
  /**
  * 解析文件
 
  * @param resource
  * @return
  */
  public static FileObject resolveFile(String resource) {
  FileObject fileObject = fileObjectCacheMap.get(resource);
  if (fileObject != null ) {
  long oldTime = fileModifyTimeMap.get(resource);
  long newTime = fileObject.getLastModifiedTime();
  if (oldTime == newTime) {
  return fileObject;
  }
  }
  try {
  resource = URLDecoder.decode(resource, "UTF-8" );
  } catch (UnsupportedEncodingException e) {
  // 如果出错也不用管,忽略之
  }
  SchemaProvider schemaProvider = schemaProviderMap.get(defaultSchema);
  for (SchemaProvider provider : schemaProviderMap.values()) {
  if (provider.isMatch(resource)) {
  schemaProvider = provider;
  break ;
  }
  }
  fileObject = schemaProvider.resolver(resource);
  fileObjectCacheMap.put(resource, fileObject);
  fileModifyTimeMap.put(resource, fileObject.getLastModifiedTime());
  return fileObject;
  }
  /**
  * 解析URL
 
  * @param url
  * @return
  */
  public static FileObject resolveURL(URL url) {
  return resolveFile(url.getPath());
  }
}

可以看到,他实际上是一个工具类,提供了若干个静态方法,以供使用,因此,为了便于扩展,此类也没有加final,考虑到添加模式提供者一般是加载时单线程处理的,因此,为了提高效率,也没有增加线程安全相关的处理。

主体就是这三个类,接下来就是FileObject和SchemaProvider的扩展了,从下面的代码可以看到,默认已经添加了Jar文件支持,Zip文件支持,本地文件支持,Http资源支持,Ftp支持,JBossVfsSchema支持。

?
1
2
3
4
5
6
7
8
9
static {
  addSchemaProvider( new JarSchemaProvider());
  addSchemaProvider( new ZipSchemaProvider());
  addSchemaProvider( new FileSchemaProvider());
  addSchemaProvider( new HttpSchemaProvider());
  addSchemaProvider( new HttpsSchemaProvider());
  addSchemaProvider( new FtpSchemaProvider());
  addSchemaProvider( new JBossVfsSchemaProvider());
  }
当然,你也可以根据进行进行扩展,只要调用VFS类的addSchemaProvider方法就可以了。限于篇幅,SchemaProvider及FileObject的实现类就不再一个一个讲解了,需要的话,可以看源码。接下来看看如何使用?

编写下面两行程序:

?
1
2
FileObject fileObject= VFS.resolveFile( "src/main/java/org/tinygroup/vfs" );
FileUtils.printFileObject(fileObject);
输出结果如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
absolutePath:E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs
extName:null
fileName:vfs
inputsteam:null
path:
provider:org.tinygroup.vfs.impl.FileSchemaProvider@296672d6
size:0
children:[E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\FileObject.java, E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\impl, E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\SchemaProvider.java, E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\util, E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\VFS.java]
parent:null
exist:true
folder:true
url:file:E:/SVN/tinyorg-code/trunk/Sources/framework/base/vfs/src/main/java/org/tinygroup/vfs
------------------
absolutePath:E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs\FileObject.java
extName:java
fileName:FileObject.java
inputsteam:java.io.BufferedInputStream@13ccb029
path:/FileObject.java
provider:org.tinygroup.vfs.impl.FileSchemaProvider@296672d6
size:2077
children:null
parent:E:\SVN\tinyorg-code\trunk\Sources\framework\base\vfs\src\main\java\org\tinygroup\vfs
exist:true
folder:false
url:file:E:/SVN/tinyorg-code/trunk/Sources/framework/base/vfs/src/main/java/org/tinygroup/vfs/FileObject.java
------------------
下面还有许多文件......省略了

可以看到确实是已经成功解析本地文件。

解析Web页面也是很方便的,如下:

?
1
2
3
FileObject fileObject=VFS.resolveFile( "http://my.oschina.net/tinyframework" );
assertTrue(fileObject instanceof HttpFileObject);
FileUtils.printFileObject(fileObject);
测试用例也通过,说明Web页面也是一样样的。

由于所有的文件对象都实现了FileObject接口,所以,我们成功的对所有的文件进行了封装,构建了我们自己的VFS。

相关文章
|
存储 缓存 Linux
sysfs文件系统(1)
sysfs是一种虚拟文件系统,旨在提供一种访问内核数据结构的方法,从而允许用户空间程序查看和控制系统的设备和资源。sysfs文件系统通常被挂载在/sys目录下。sysfs提供了一种以树状结构组织的系统信息的方式,其中每个设备都有一个唯一的目录来表示它自己,其中包含有关设备的各种属性和状态信息的文件。这些文件通常是只读的,但有些也可以用于修改设备的某些设置。sysfs还提供了一个机制来通知用户空间程序有关设备状态更改的信息,从而使其能够对这些更改做出反应。sysfs文件系统被广泛用于Linux内核中,它为开发者提供了一种简单的方式来管理和控制系统中的各种设备和资源。
158 0
|
4月前
|
存储 数据可视化 Linux
proc文件系统怎么玩?
proc文件系统怎么玩?
74 0
|
Linux 程序员 Shell
sysfs文件系统与kobject
sysfs文件系统与kobject
124 0
|
存储 缓存 Linux
从 lsof 开始,深入理解虚拟文件系统
从 lsof 开始,深入理解虚拟文件系统
62 0
|
Unix Linux 容器
sysfs文件系统(2)
每个内核设备直接或间接嵌入kobject属性。在添加到系统之前,必须使用kobject_ create()函数分配kobject,并将已经分配但尚未初始化的kob ject指针及其kobject_type 指针作为参数。kobject_add()函数用于添加kobject并将其链接到系统,同时根据其层次结构创建目录及其默认属性。功能与之相反的函数是kobject_ del(),将kobject删除链接
105 0
|
Unix Linux 程序员
|
存储 缓存 安全
基于FUSE的简单文件系统
基于FUSE的简单文件系统
525 0