虚拟文件系统VFS

本文介绍了一种自建的虚拟文件系统(VFS)框架,该框架解决了Apache VFS存在的内存泄漏问题。文中详细阐述了VFS的设计思路,包括核心接口和类的定义,并展示了如何使用该框架处理本地文件和Web资源。

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值