知识点来自:

1.javase中的类加载路径

2.java.io中的基本api类


前言:

        最近负责一个代码生成器的项目,还要做成一个eclipse插件的形式,后期还要做成可视化的方式。本身项目所要求的技术点不难,需要用到大量的文件读写,文件复制,已经文件所在位置的查找,读写以及复制比较好说,前提就是查找文件所在位置这点,故此有了此篇文章。


先来说一个概念:

URL:是网络中存在的一个资源文件,表示统一资源定位符号(uniform resource locator URL),表示toString的形式一般是:file:/D:/Day1.java或者jar:/D:/Day1.java,还有或者是其他的,例如笔者所做的项目中URL表示形式:bundleresource://709.fwk15218962:1/META-INF/MANIFEST.MF


File:表示的是一个文件,可以是目录,也可以是文件夹,表示toString的形式是:D:\Day1.java


以上2点很概念重要,对于子个项目进行集成时候。


进入正题:

读取项目中的文件(位置):

maven项目中的目录结构以及真实发布后的结构如下:

如何阅读Java项目代码_jar


我们要读的是红线画圈的所表示的文件,实际打包后要读取的是target中的打包中文件所在的位置,推荐方式使用java的class所在方式进行读取。

使用类加载的方式读取文件的好处是能读取到classes中该类所在的位置,在在同级,上级,下级目录或者文件就简单容易很多。


细节如下:

1.getResource()


URL resource = MyFile.class.getResource("");
		URL resource1 = MyFile.class.getResource("/");



打印结果:


file
file:/D:/Users/eclipse-git/File/target/classes/com/lgy/file/
--------------------
file
file:/D:/Users/eclipse-git/File/target/classes/

说明:

不加/和加/的区别:前者查找出类所在的绝对路径,后者加/是查找classes下的路径,/com则是查找classes下的com路径,若没有则会抛出空指针异常。

注意,在maven项目汇总的test目录下进行测试时候会发现,resource1的打印结果会是file:/D:/Users/eclipse-git/File/target/test-classes/,但是项目的真实环境不会出现,没人会把test下的文件拿出来使用。


2.getClassLoader

与上面类似。只不过相反,不做解释


对应的读取文件位置就简单多了:


URL resource = MyFile.class.getResource("/");
		String path = resource.getPath();
		System.out.println(path);
		File f = new File(path+ "bbb.txt") ;
		System.out.println(f.exists());



打印结果为true

这种方式真是是一劳永逸吗?


--------------------------------------------------------------------------------jar篇幅-------------------------------------------------------------------------------------------------------------------------------------

尝试着把你那个项目使用maven依赖,供其他子项目进行使用,你发现原来读写那个文件是好使的变成不好使不?依赖的pom文件如下:


<dependency>
			<groupId>com.lgy</groupId>
			<artifactId>File</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>





如何阅读Java项目代码_System_02




之前File项目中有一个MyFile测试方法内容如下:

package com.lgy.file;

import java.io.File;
import java.net.URL;

public class MyFile {
	public static void main(String[] args) {
		MyFile.test();
	}
	
	public static void test() {
		//测试com/lgy/file 中的ccc.txt文件
		URL resource = MyFile.class.getResource("/com/lgy/file/");
		String path = resource.getPath();
		System.out.println(resource.getProtocol());
		System.out.println(path);
		File f = new File(path+ "ccc.txt") ;
		System.out.println(f.exists());
		
		System.out.println("**************************");
		//测试resources中的文件
		URL resource1 = MyFile.class.getResource("/");
		String path1 = resource1.getPath();
		System.out.println(resource1.getProtocol());
		System.out.println(path1);
		File f1 = new File(path1+ "aaa.txt") ;
		System.out.println(f1.exists());
	}
}



打印结果:

file
/D:/Users/eclipse-git/File/target/classes/com/lgy/file/
true
**************************
file
/D:/Users/eclipse-git/File/target/classes/
true



一个是类路径下放的一个文件,一个是resource下存放的一个文件,都有。此时在新的项目TestMaven下面执行该方法,结果如下:

file
/D:/Users/eclipse-git/File/target/classes/com/lgy/file/
true
**************************
file
/D:/Users/eclipse-git/TestMaven/target/classes/
false



可以看到放在类路径下的文件找到了打印为True,而在resource下的文件确找不到打印却为了fasle。

分析原因,不难找出, MyFile.class.getResource("/")获取resources根路径时候找的是本项目的,此项目若没有则不会在其他依赖中的项目进行查找。


问题?若其他项目放在了resources下的文件如何查找呢?

带着这个问题,试着将之前的那个文件打包成jar,供另一个maven项目使用。此时的项目目录结构如下:

如何阅读Java项目代码_System_03

同样的MyFile测试方法方法,执行结果如何呢?

jar
file:/D:/Users/eclipse-git/File/target/File.jar!/com/lgy/file/
false
**************************
file
/D:/Users/eclipse-git/TestMaven/target/classes/
false



分析结果:都为false文件查找不存在,第二个还是查找本项目中的resources下的文件所有查找不到,但是第一个打印的是什么?jar,回头看看打印出jar这个函数:

URL resource = MyFile.class.getResource("/com/lgy/file/");
System.out.println(resource.getProtocol());


没错就是URL中的protocol,URL中的协议类型说明了此资源定位符所在的协议类型是什么,此时打印出来的jar恰巧说明了文件在jar中。若要读取该下的文件如何读取呢?

方式一:

类加载起中有一种加载方式可以获取到jar下的文件,以流的方式:

InputStream in = Test.class.getResourceAsStream("/com/lgy/file/ccc.txt");



此时并可读到在类路径下的ccc.txt下的内容文件。



方式二:

如果文件的协议是jar,则用解析jar的方式进行:

if (url.getProtocol().equalsIgnoreCase("jar")) {
			String path = MyFile.class.getProtectionDomain().getCodeSource().getLocation().getPath();
			
			try {
				JarFile jf = new JarFile(path);
				Enumeration<JarEntry> e = jf.entries();
				while (e.hasMoreElements()) {
					JarEntry je = e.nextElement();
					//获取jar中文件的名称
					if (je.getName().startsWith("com/lgy/file/ccc.txt")) {
						System.out.println(je.getName());
						System.out.println(je.isDirectory());
						System.out.println(changeInpstream2String(jf.getInputStream(je)));
					}
				}
				jf.close();
				
			} catch (IOException e) {
				e.printStackTrace();
			}
		}



最后关于URL的一个问题,笔者在制作eclipse plugin的时候遇到读取file是的协议是:bundleresource,解决思路是:

package com.gsafety.cloudframework.eclipseurl;

import java.io.IOException;
import java.net.URL;

import org.eclipse.core.runtime.FileLocator;

public class EclipseLocator implements Locator {
	public URL resolve(URL url)  {
		try {
			return FileLocator.resolve(url);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return url;
	}
}



使用eclipse中提供的api转换为新的URL,此时的URL是的协议类型是jar,在解析的话就容易许多。