您的位置:首页 > 其它

Web基础之使用URL访问资源

2016-11-07 18:26 344 查看

前言

在一些框架中经常看到使用URL访问项目中的资源,这篇文章简单的梳理了一下这个URL的相关内容

正文

URL(Uniform Resource Locator)中文名为统一资源定位符

具体格式如下

protocol://host:port/path?query#ref


这里protocol可以是下面的内容:HTTP, HTTPS, FTP, 和File;port 为端口号;path为文件路径及文件名

使用URL获取文件系统资源

//context是ServletContext
String absolutePath = context.getRealPath("/WEB-INF/classes/utf8.txt");
URL url = new URL("file",null,absolutePath); //absoltePath是普通的文件格式
URLConnection  connection = url.openConnection();
InputStream is = connection.getInputStream();
FileUtil.readFileWithInputStream(is, "utf8");


需要注意几点地方

getRealPath获取的是项目中的指定资源在文件系统中的完整路径,以“/”开头表示Web项目的根目录(http://host:port/webName 下的目录(URL)

当前项目用maven多模块构建,读取的资源在一个maven模块中,也就是说部署到tomcat时,资源是在jar包,所以上面指定的路径会找不到,解决办法是获取jar的URL路径(用classloader加载获取jar的URL),然后使用JarURLConnection读取,或者将资源移动到maven项目的webapp所在的模块的src/main/resources中,在部署时会将资源放到classpath下

url.openConnection()的实例可能是FileURLConnection,HttpURLConnection,JarURLConnection还有其它几种,这里协议是file,所以返回的是FileURLConnection的实例,但是FileURLConnection这个类在java中不能找到,所以使用父类来引用,可以完成简单的操作

URL url = new URL(path)这里path必须是URL协议格式,如果是普通的文件路径,那么可以按照上面的3个参数构造函数创建对象也可以使用
new File(resourceLocation).toURI().toURL()
返回URL

获取jar包的URL

使用JarFile来完成jar操作

public static void obtainJarFile(String path) {
try {
JarFile jarFile = new JarFile(path); //jar包路径,创建一个jarFile对象
System.out.println(String.format("%s", jarFile.getName()));
Enumeration<JarEntry> entrys = jarFile.entries();//jarFile提供操作jar包的方法
while(entrys.hasMoreElements()) { //会从上到下,从外到里一个一个得到目录以及目录里面的文件
JarEntry entry = entrys.nextElement();
System.out.println(String.format("-%s",entry.getName()));
}
} catch (IOException e) {
e.printStackTrace();
}
}


需要注意的几点:

path是jar包所在的路径,可以是完整的文件系统路径,也可以是相对于工程的相对路径

枚举会将所有jar包的文件一一列举出来,包括目录和文件,如果只对文件感兴趣可以
entry.isDirectory();
来判断

使用classLoader来完成对jar的操作

需求:运行在tomcat容器中的项目,需要获取classpath下的config目录资源(包括WEB-INF/lib中jar里面的config资源,也包括WEB-INF/classes中的config目录的资源)

可行性:上面的需求通过分析发现可以用加载器加载资源来实现:因为web的加载器(WebAppClassLoader)会加载WEB-INF/lib和WEB-INF/classes目录下资源,只要传递相对于上面两个根目录的相对路径就能获取到需要的资源

抽象:将上面的业务抽象成算法如下

得到当前的ClassLoader,因为运行在Tomcat容器中,所以是Tomcat提供的加载器

通过外面给予的相对于加载器classpath的相对路径加载资源,使用getResources(path)来完成

得到资源的URL可能是file:这种格式也可能是jar:格式,如果是file则将URL转成磁盘的文件系统路径,然后用File来读取资源。如果是jar则使用JarURLConnection 来获取JarFile来操作

如果是jar:的URL,先获取jar包下的所有路径,在通过一一对比过滤得到指定目录下的文件,然后使用URL的openStream操作流读取资源

如果是file:的URL,先将URL转成URI然后得到磁盘文件路径,使用File来读取指定目录下的文件资源

代码示例

public static void obtainJarFileWithClassLoader(String path) {
try {
ClassLoader loader = ClassLoaderUtil.getCurrentClassLoader();
Enumeration<URL> urls = loader.getResources(path);
while(urls.hasMoreElements()) {
URL url = urls.nextElement();
URLConnection  connection = url.openConnection();
//使用父类URLConnection可以做一些简单的操作,
//但是这里要做jar复杂一点的操作,明显不够用,所以转成JarURLConnection
if(connection instanceof JarURLConnection) {
JarURLConnection jarConn = (JarURLConnection) connection;
URL jarPath = jarConn.getJarFileURL();//获取jar包所在的路径
System.out.println(String.format("jar包在文件系统中的路径:%s", jarPath.toExternalForm())); //toExternalForm作用URL转成String
JarFile jarFile = jarConn.getJarFile(); //获取jar包
JarEntry jarEntry = jarConn.getJarEntry(); //获取当前连接目录的路径
System.out.println(String.format("当前连接目录在jar的相对位置%s", jarEntry.getName()));
//获取jar包下的文件,并加载资源
Enumeration<JarEntry> entrys = jarFile.entries();
while(entrys.hasMoreElements()) {
JarEntry entry = entrys.nextElement();
if(!entry.isDirectory()) {
String relativePath = entry.getName();//文件在jar下的路径
if(relativePath.startsWith(jarEntry.getName())) { //只获取config目录下的资源,url包含了config目录,所以要将relativePath中的config截取掉
URL perResource = new URL(url,relativePath.substring(jarEntry.getName().length())); //使用基地址和相对URL创建
//使用URL加载资源
InputStream is = perResource.openStream();
FileUtil.readFileWithInputStream(is, "utf8");
}
}
}
}else {
if(url.getProtocol().startsWith("file")) {
try {
//参考:http://blog.csdn.net/ocean1010/article/details/6114222
URI uri = new URI(url.getFile().replace(" ", "%20"));//将请求路径中的空格替换成%20
String resources = uri.getSchemeSpecificPart();//获取文件系统的路径,也就是将URL转成文件系统路径
File file = new File(resources);
FileUtil.scanFileSystemDirectory(file,""); //显示这个目录结构
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}


ignore

使用JarURLConnection得到当前jar文件:jarConn.getJarFile()

得到jar包的URL路径:jarConn.getJarFileURL()或者jarConn.getJarFile().getName()

得到当前相对路径(相对于加载器默认加载目录)下的目录,如外面传递的是“config/”它相对于WEB-INF/lib和WEB-INF/classes目录。JarEntry jarEntry = jarConn.getJarEntry(); 得到结果就是“config/”

public URL(URL context, String url) 使用基地址和相对URL创建。context是已经知道的URL,在context后面连接上指定的相对路径

将URL转成文件系统路径:先转成URI,再使用getSchemeSpecificPart获取文件系统路径

加载jar包下的class

项目使用maven多模块构建,因此每个模块会打成jar包放到WEB-INF/lib下面,那么如何加载jar下面的class呢?

这里需要用到加载器,上面讲过了WEB的加载器会加载WEB-INF/lib下的资源,因此这里只要用WebappClassLoader加载器加载资源即可

/**
- 加载jar包下的class
- @param path org.easyutil.http.HttpClientUtil
- @param clazz HttpClientUtil.class
*/
public static void loadJarClass(String path ,Class<?> clazz) {
ClassLoader loader = ClassLoaderUtil.getCurrentClassLoader();
try {
//loader加载器和加载当前类FileUtil的加载器相同,所以这里可以赋值
//参考:http://blog.sina.com.cn/s/blog_56d8ea90010127ks.html
clazz = loader.loadClass(path);
System.out.println(clazz.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}


ignore

Class<?> clazz = loader.loadClass(path);
必须保证加载左右两边的两个加载器相同。代码在(编译?执行?)时用当前加载器(从线程中获取?)加载Class尖括号中的类型的class,而后面loader又是我一个新的加载器,因此需要保证这两个加载器必须相同才不会报异常

只要选择对的加载器,就能加载指定目录下的资源
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  web