# java项目资源文件的存放与读取
## 1.写项目时的文件访问
文件的访问路径有两种:以“/”开头的是绝对路径,没有则是相对路径。
这里我以myeclipse项目结构作为例子:
D:\JAVAPRO\PACK
│ .classpath
│ .project
│
├─.settings
│ org.eclipse.jdt.core.prefs
│
├─bin
│ │ instruction.txt
│ │
│ ├─com
│ │ GetResource.class
│ │ instruction.txt
│ │
│ └─resource
│ instruction.txt
│
├─resource
│ instruction.txt
│
└─src
│ instruction.txt
│
├─com
│ GetResource.java
│
└─resource
instruction.txt
此时的txt文件的相对路径参照是项目文件夹D:\JAVAPRO\PACK,访问方式分别为:
1.项目文件夹下的resource目录下
```
File file=new File("resource/instruction.txt");
```
2.src目录下
```
File file=new File("src/instruction.txt");
```
3.src的resource包下
```
File file=new File("src/resource/instruction.txt");
```
**注意:此时可以通过绝对路径“D:/JavaPro/Pack/src/instruction.txt”访问得到,但一旦文件系统目录改变将无法访问,因此一般不推荐这样去访问项目资源文件。**
## 2.项目打包后的文件访问
将上述的项目打包成jar后,文件的目录结构为:
C:\USERS\RUNNING\DESKTOP\PACK
│ instruction.txt
│
├─com
│ GetResource.class
│ instruction.txt
│
├─META-INF
│ MANIFEST.MF
│
└─resource
instruction.txt
**注意,项目文件夹下的resource并没被一起打包进去,这里的resource是src下的那个resource,当然了,也可以通过解压软件将resource给注入进去。**
此时的相对路径参照是jar的所在文件夹,因此只要将资源文件或文件夹与jar放在一起即可正常访问,或许有人想通过绝对路径进入jar的里面来访问,但也失败了,原因是jar只是个文件,并不是文件夹。
难道就没有办法来访问jar内的文件了吗?当然不是,我们可以用类装载器(ClassLoader)来做到这一点:
1. **ClassLoader 是类加载器的抽象类。它可以在运行时动态的获取加载类的运行信息。** 可以这样说,当我们调用jar中的Resource类时,JVM加载进Resource类,并记录下Resource运行时信息(包括Resource所在jar包的路径信息)。而ClassLoader类中的方法可以帮助我们动态的获取这些信息:
- public URL getResource(String name)
查找具有给定名称的资源。资源是可以通过类代码以与代码基无关的方式访问的一些数据(图像、声音、文本等)。并返回资源的URL对象。
- public InputStream getResourceAsStream(String name);
返回读取指定资源的输入流。这个方法很重要,可以直接获得jar包中文件的内容。
2. ClassLoader是abstract的,不可能实例化对象,更加不可能通过ClassLoader调用上面两个方法。**所以我们真正写代码的时候,是通过Class类中的getResource()和getResourceAsStream()方法,这两个方法会委托ClassLoader中的getResource()和getResourceAsStream()方法,要注意的是这两个方法都不能在静态方法中运行** 。
既然getResource()可以返回URL,那来试试:
```
package com;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import javax.swing.JOptionPane;
public class GetResource {
public String getResource(String s){
// 查找指定资源的URL,其中instruction.txt仍然开始的src/resource目录下
URL fileURL = this.getClass().getResource(s);
System.out.println(fileURL.getFile());
String path = null;
try {
path=fileURL.toURI().toString();
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return path;
}
public static void main(String[] args) {
GetResource res=new GetResource();
System.out.println(new GetResource().getResource(""));
System.out.println(new GetResource().getResource("/resource/instruction.txt"));
}
}
```
```
打包前的输出为:
file:/D:/JavaPro/Pack/bin/com/instruction.txt
file:/D:/JavaPro/Pack/bin/resource/instruction.txt
打包后的输出为:
jar:file:/C:/Users/Running/Desktop/Design.jar!/com/instruction.txt
jar:file:/C:/Users/Running/Desktop/Design.jar!/resource/instruction.txt
```
以上表示已经动态获取到了文件资源的URL,那可以直接拿这个URL进行访问呢?
答案是否定的因为其并不是文件资源定位符的格式 (而是jar[中资源](https://www.baidu.com/s?wd=%E4%B8%AD%E8%B5%84%E6%BA%90&tn=24004469_oem_dg&rsv_dl=gh_pl_sl_csd)有其专门的URL形式: **jar:<url>!/{entry}** ),**但如果是图片的话,是可以通过得到的URL来获取到的**:
```
package com;
import java.io.*;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class GetResource {
private static InputStream in;
public URL getResource(String s) {
URL fileURL = this.getClass().getResource(s);
return fileURL;
}
public static void main(String[] args) {
GetResource res = new GetResource();
URL file = res.getResource("/resource/bg.jpg");
JFrame f = new JFrame();
f.getContentPane().add(new JLabel(new ImageIcon(file)));
f.setSize(500, 400);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
```
**重点**:我们不能用常规操作文件的方法来读取jar中的资源文件,**但可以通过Class类的getResourceAsStream()方法来获取** ,这种方法是如何读取jar中的资源文件的,这一点对于我们来说是透明的。
```
public String getstr(String name){
InputStream is=this.getClass().getResourceAsStream("/resource/"+name);
String laststrJson = "";
BufferedReader reader;
try {
reader = new BufferedReader(new InputStreamReader(is));
String tempString = null;
int line = 1;
// 一次读入一行,直到读入null为文件结束
while ((tempString = reader.readLine()) != null) {
laststrJson = laststrJson + tempString+"\r\n";
line++;
}
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return laststrJson;
}
```
总结:只要将资源文件或文件包放在src下并使用如上方法访问,系统就会打包到jar中并在运行时正常访问(将jar用解压软件打开并注入资源文件或文件夹也可)。
以下附上java资源的各种读取与写入方法:
```
import java.io.*;
public class ReadFromFile {
/**
* 以字节为单位读取文件,常用于读二进制文件,如图片、声音、影像等文件。
*/
public static void readFileByBytes(String fileName) {
File file = new File(fileName);
InputStream in = null;
try {
System.out.println("以字节为单位读取文件内容,一次读一个字节:");
// 一次读一个字节
in = new FileInputStream(file);
int tempbyte;
while ((tempbyte = in.read()) != -1) {
System.out.write(tempbyte);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
return;
}
try {
System.out.println("以字节为单位读取文件内容,一次读多个字节:");
// 一次读多个字节
byte[] tempbytes = new byte[100];
int byteread = 0;
in = new FileInputStream(fileName);
ReadFromFile.showAvailableBytes(in);
// 读入多个字节到字节数组中,byteread为一次读入的字节数
while ((byteread = in.read(tempbytes)) != -1) {
System.out.write(tempbytes, 0, byteread);
}
} catch (Exception e1) {
e1.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
}
}
}
}
/**
* 以字符为单位读取文件,常用于读文本,数字等类型的文件
*/
public static void readFileByChars(String fileName) {
File file = new File(fileName);
Reader reader = null;
try {
System.out.println("以字符为单位读取文件内容,一次读一个字节:");
// 一次读一个字符
reader = new InputStreamReader(new FileInputStream(file));
int tempchar;
while ((tempchar = reader.read()) != -1) {
// 对于windows下,\r\n这两个字符在一起时,表示一个换行。
// 但如果这两个字符分开显示时,会换两次行。
// 因此,屏蔽掉\r,或者屏蔽\n。否则,将会多出很多空行。
if (((char) tempchar) != '\r') {
System.out.print((char) tempchar);
}
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println("以字符为单位读取文件内容,一次读多个字节:");
// 一次读多个字符
char[] tempchars = new char[30];
int charread = 0;
reader = new InputStreamReader(new FileInputStream(fileName));
// 读入多个字符到字符数组中,charread为一次读取字符数
while ((charread = reader.read(tempchars)) != -1) {
// 同样屏蔽掉\r不显示
if ((charread == tempchars.length) && (tempchars[tempchars.length - 1] != '\r')) {
System.out.print(tempchars);
} else {
for (int i = 0; i < charread; i++) {
if (tempchars[i] == '\r') {
continue;
} else {
System.out.print(tempchars[i]);
}
}
}
}
} catch (Exception e1) {
e1.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
}
}
}
}
/**
* 以行为单位读取文件,常用于读面向行的格式化文件
*/
public static void readFileByLines(String fileName) {
File file = new File(fileName);
BufferedReader reader = null;
try {
System.out.println("以行为单位读取文件内容,一次读一整行:");
reader = new BufferedReader(new FileReader(file));
String tempString = null;
int line = 1;
// 一次读入一行,直到读入null为文件结束
while ((tempString = reader.readLine()) != null) {
// 显示行号
System.out.println("line " + line + ": " + tempString);
line++;
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
}
}
}
}
/**
* 随机读取文件内容
*/
public static void readFileByRandomAccess(String fileName) {
RandomAccessFile randomFile = null;
try {
System.out.println("随机读取一段文件内容:");
// 打开一个随机访问文件流,按只读方式
randomFile = new RandomAccessFile(fileName, "r");
// 文件长度,字节数
long fileLength = randomFile.length();
// 读文件的起始位置
int beginIndex = (fileLength > 4) ? 4 : 0;
// 将读文件的开始位置移到beginIndex位置。
randomFile.seek(beginIndex);
byte[] bytes = new byte[10];
int byteread = 0;
// 一次读10个字节,如果文件内容不足10个字节,则读剩下的字节。
// 将一次读取的字节数赋给byteread
while ((byteread = randomFile.read(bytes)) != -1) {
System.out.write(bytes, 0, byteread);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (randomFile != null) {
try {
randomFile.close();
} catch (IOException e1) {
}
}
}
}
/**
* 显示输入流中还剩的字节数
*/
private static void showAvailableBytes(InputStream in) {
try {
System.out.println("当前字节输入流中的字节数为:" + in.available());
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String fileName = "C:/temp/newTemp.txt";
ReadFromFile.readFileByBytes(fileName);
ReadFromFile.readFileByChars(fileName);
ReadFromFile.readFileByLines(fileName);
ReadFromFile.readFileByRandomAccess(fileName);
}
}
```
```
public class AppendToFile {
/**
* A方法追加文件:使用RandomAccessFile
*/
public static void appendMethodA(String fileName, String content) {
try {
// 打开一个随机访问文件流,按读写方式
RandomAccessFile randomFile = new RandomAccessFile(fileName, "rw");
// 文件长度,字节数
long fileLength = randomFile.length();
//将写文件指针移到文件尾。
randomFile.seek(fileLength);
randomFile.writeBytes(content);
randomFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* B方法追加文件:使用FileWriter
*/
public static void appendMethodB(String fileName, String content) {
try {
//打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
FileWriter writer = new FileWriter(fileName, true);
writer.write(content);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String fileName = "C:/temp/newTemp.txt";
String content = "new append!";
//按方法A追加文件
AppendToFile.appendMethodA(fileName, content);
AppendToFile.appendMethodA(fileName, "append end. \n");
//显示文件内容
ReadFromFile.readFileByLines(fileName);
//按方法B追加文件
AppendToFile.appendMethodB(fileName, content);
AppendToFile.appendMethodB(fileName, "append end. \n");
//显示文件内容
ReadFromFile.readFileByLines(fileName);
}
}
```
网友评论