知识点来自:
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项目中的目录结构以及真实发布后的结构如下:
我们要读的是红线画圈的所表示的文件,实际打包后要读取的是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>
之前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项目使用。此时的项目目录结构如下:
同样的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,在解析的话就容易许多。