Maven引用坐标:
org.tinygroup
vfs
0.0.12
一开始,本人抱着对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文件等等,其接口声明如下:
/**
* 模式提供者接口
*/
public interface SchemaProvider {
/**
* 是否匹配
*
* @param resource
* @return 如果返回true,表示此提供者可以处理,返回false表示不能处理
*/
boolean isMatch(String resource);
/**
* 返回处理的模式
*
* @return
*/
String getSchema();
/**
* 解析资源,并返回文件对象
*
* @param resource
* @return
*/
FileObject resolver(String resource);
}
文件对象FileObject,是对文件的访问的统一接口,它的定义如下:
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 getChildren();// 返回下级文件列表
FileObject getChild(String fileName);//获取参数名称指定的fileobject
long getLastModifiedTime();// 返回修改时间
SchemaProvider getSchemaProvider();//返回模式提供者
boolean isInPackage();//是否是包文件
URL getURL();//返回url
OutputStream getOutputStream();//返回输出流
} 关键的接口就是上面两个,当然还有一个重要的类就是虚拟文件系统类,内容如下:
/**
* 虚拟文件系统
*/
public class VFS {
static Map fileObjectCacheMap = new ConcurrentHashMap();
static Map fileModifyTimeMap = new ConcurrentHashMap();
static final Map schemaProviderMap = new HashMap();
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支持。
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的实现类就不再一个一个讲解了,需要的话,可以看源码。接下来看看如何使用?
编写下面两行程序:
FileObject fileObject= VFS.resolveFile("src/main/java/org/tinygroup/vfs");
FileUtils.printFileObject(fileObject); 输出结果如下:
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页面也是很方便的,如下:
FileObject fileObject=VFS.resolveFile("http://my.oschina.net/tinyframework");
assertTrue(fileObject instanceof HttpFileObject);
FileUtils.printFileObject(fileObject); 测试用例也通过,说明Web页面也是一样样的。
由于所有的文件对象都实现了FileObject接口,所以,我们成功的对所有的文件进行了封装,构建了我们自己的VFS。
本文介绍了作者因Apache VFS内存泄露问题,自主研发的Java虚拟文件系统(VFS)。该系统包括模式提供者接口、FileObject接口和VFS类,支持本地、HTTP、FTP等文件访问。通过自定义SchemaProvider和FileObject实现,解决了原有的问题,并提供了类似的文件操作功能。
771

被折叠的 条评论
为什么被折叠?



