Web基础之使用URL访问资源

来源:互联网 发布:yy是啥软件 编辑:程序博客网 时间:2024/06/05 02:34

前言

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

正文

URL(Uniform Resource Locator)中文名为统一资源定位符
具体格式如下

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

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

使用URL获取文件系统资源

//context是ServletContextString 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");

需要注意几点地方

  1. getRealPath获取的是项目中的指定资源在文件系统中的完整路径,以“/”开头表示Web项目的根目录(http://host:port/webName 下的目录(URL)
  2. 当前项目用maven多模块构建,读取的资源在一个maven模块中,也就是说部署到tomcat时,资源是在jar包,所以上面指定的路径会找不到,解决办法是获取jar的URL路径(用classloader加载获取jar的URL),然后使用JarURLConnection读取,或者将资源移动到maven项目的webapp所在的模块的src/main/resources中,在部署时会将资源放到classpath下
  3. url.openConnection()的实例可能是FileURLConnection,HttpURLConnection,JarURLConnection还有其它几种,这里协议是file,所以返回的是FileURLConnection的实例,但是FileURLConnection这个类在java中不能找到,所以使用父类来引用,可以完成简单的操作
  4. 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();        }    }

需要注意的几点:

  1. path是jar包所在的路径,可以是完整的文件系统路径,也可以是相对于工程的相对路径
  2. 枚举会将所有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目录下资源,只要传递相对于上面两个根目录的相对路径就能获取到需要的资源
抽象:将上面的业务抽象成算法如下

  1. 得到当前的ClassLoader,因为运行在Tomcat容器中,所以是Tomcat提供的加载器
  2. 通过外面给予的相对于加载器classpath的相对路径加载资源,使用getResources(path)来完成
  3. 得到资源的URL可能是file:这种格式也可能是jar:格式,如果是file则将URL转成磁盘的文件系统路径,然后用File来读取资源。如果是jar则使用JarURLConnection 来获取JarFile来操作
  4. 如果是jar:的URL,先获取jar包下的所有路径,在通过一一对比过滤得到指定目录下的文件,然后使用URL的openStream操作流读取资源
  5. 如果是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

  1. 使用JarURLConnection得到当前jar文件:jarConn.getJarFile()
  2. 得到jar包的URL路径:jarConn.getJarFileURL()或者jarConn.getJarFile().getName()
  3. 得到当前相对路径(相对于加载器默认加载目录)下的目录,如外面传递的是“config/”它相对于WEB-INF/lib和WEB-INF/classes目录。JarEntry jarEntry = jarConn.getJarEntry(); 得到结果就是“config/”
  4. public URL(URL context, String url) 使用基地址和相对URL创建。context是已经知道的URL,在context后面连接上指定的相对路径
  5. 将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又是我一个新的加载器,因此需要保证这两个加载器必须相同才不会报异常
  • 只要选择对的加载器,就能加载指定目录下的资源
0 0