1. java操作文件
操作文件,分为两种操作:
(1)向目标位置写入文件;
(2)从目标位置读取(加载)文件。
其中写入文件比较简单,因为既然我们要写入文件,目标位置肯定是知道的,肯定是一个文件系统路径。这时候我们只需要传入绝对路径或者相对路径(相对某个工作路径的路径,比如相对classpath的路径,相对项目工作根目录的路径),就能将文件写入到指定位置。
但从目标位置加载文件就比较麻烦,如果目标文件就是在文件系统上,那还好,指定绝对路径或者相对路径去读取即可。
但如果目标文件是在其他位置呢,比如在jar包中,这时候目标文件的位置实际上就不是一个磁盘文件系统的路径了。
就像springboot中加载各个jar包中的spring.factories属性文件一样,它肯定不是直接通过绝对路径或者相对路径去加载的了,因为这时候目标文件的路径就不是一个文件路径。
这也正是我们这篇文章要分析的重点。
如果只是从文件系统加载文件,直接使用:
File file = new File(filePath);
其中,filePath取值如下:
绝对路径:windows中必须以盘符开始,比如"D:",linux系统中必须以根路径开始,比如"/";
相对路径:以\开始,或者以.\开始,或者直接开始(忽略\和.\,但不能是盘符开始),这种方式传入的路径默认就是相对路径。
如果牵扯到从jar包中加载目标文件,那么方式就变得比较复杂,我们继续往下看!
2. 加载文件离不开class和classLoader
在java中我们要加载文件,最常见的方式就是使用File去new一个文件对象,这样就能通过文件对象来操作磁盘上的目标文件资源。
但这里面最核心的问题是,File file = new File(path); 中需要path。
所以,最核心的问题就是如何获取到我们需要操作的目标文件的path。
如果我们知道目标文件的绝对路径还好说,但是一般在工程中我们只知道目标文件的相对路径,而没法知道目标文件在磁盘上的绝对路径到底是啥。
因此,我们需要对目标文件进行资源定位。
java中对资源的定位,其底层都离不开classLoader。
因为classLoader实际上就是负责向JVM内存中装载所有class以及其他相关资源的。
可以认为,JVM中所有的东西都是通过classLoader装载进去的。
同样,java中读取文件,文件也是资源的一种。
因此,java定位文件资源也离不开classLoader。
一般来说,定位文件经常使用两种方式,一种是通过class对象去做,一种是通过classLoader对象去做。
下面我们分析下这两种方式。
class和classLoader定位文件有和不同?
2.1 测试class的getResource()方法
创建一个LoaderUtils.java类:
package code08handlefile;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
// 学习java中定位目标文件的两种方式
public class LoaderUtils {
@Test
public void testClass() {
// /C:/D/zhaoehcode-java/springboot/target/classes/code08readfile/
System.out.println("传入空串:\n" + LoaderUtils.class.getResource(""));
// file:/C:/D/gitsource/zhaoehcode-java/springboot/target/test-classes/
System.out.println("传入/:\n" + LoaderUtils.class.getResource("/"));
// null
System.out.println("传入相对路径test.loader:\n" + LoaderUtils.class.getResource("test.loader"));
// file:/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
System.out.println("传入带/的相对路径/test.loader:\n" + LoaderUtils.class.getResource("/test.loader"));
// null
System.out.println("传入绝对路径 test.loader:\n" + LoaderUtils.class.getResource("C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader"));
}
}
运行如下:
传入空串:
file:/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/code08handlefile/
传入/:
file:/C:/D/gitsource/zhaoehcode-java/springboot/target/test-classes/
传入相对路径test.loader:
null
传入带/的相对路径/test.loader:
file:/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
传入绝对路径 test.loader:
null
(1)class.getResource()方法接收一个String字符串,返回一个java.net.URL对象。实际上java中所有资源的定位,都是通过java.net.URL对象来表示的。这个对象代表JVM内存中的一个资源标识。
它本身只用于定位资源,这个资源可以是网络资源、IO资源、类资源等。
我们此处要找的资源是一个文件。
(2)我们看下class.getResource()方法的源码:
public java.net.URL getResource(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResource(name);
}
return cl.getResource(name);
}
ClassLoader.getSystemResource(name)源码:
public static URL getSystemResource(String name) {
ClassLoader system = getSystemClassLoader();
if (system == null) {
return getBootstrapResource(name);
}
return system.getResource(name);
}
可以看出来,Class对象提供的getResource()方法底层实际上委托的是ClassLoader对象去操作的。Class方式加载资源底层实际上是对ClassLoader方式的封装。
(3)class.getResource();
如果传入”“,则返回的是指向当前class所在包(编译后)的绝对路径;
如果传入”/”,则返回指向测试class的绝对路径。
如果直接传入不带/的文件,则返回的资源为null.
如果传入带/的文件,则返回该目标文件的绝对资源路径。
如果传入绝对,返回null.
这意味着,使用class.getResource()去定位资源,我们是要传入带/的相对路径的。带/的相对路径是相对classpath而言的,默认会从classpath下查找目标资源。
2.2 测试classLoader
@Test
public void testClassLoader() {
// file:/C:/D/gitsource/zhaoehcode-java/springboot/target/test-classes/
System.out.println("传入空串:\n" + LoaderUtils.class.getClassLoader().getResource(""));
// null
System.out.println("传入/:\n" + LoaderUtils.class.getClassLoader().getResource("/"));
// file:/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
System.out.println("传入相对路径test.loader:\n" + LoaderUtils.class.getClassLoader().getResource("test.loader"));
// null
System.out.println("传入带/的相对路径/test.loader:\n" + LoaderUtils.class.getClassLoader().getResource("/test.loader"));
// null
System.out.println("传入绝对路径 test.loader:\n" + LoaderUtils.class.getClassLoader().getResource("C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader"));
}
运行:
传入空串:
file:/C:/D/gitsource/zhaoehcode-java/springboot/target/test-classes/
传入/:
null
传入相对路径test.loader:
file:/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
传入带/的相对路径/test.loader:
null
传入绝对路径 test.loader:
null
(1)classLoader.getResource()
不支持带”/”,只要带”/”返回的就是null。
传入不带”/”的资源,它直接从classpath下查找资源。
传入空串,它返回test-classes的绝对路径。
(2)使用classLoader一般来说不带/。
(3)传入绝对路径,返回null.
(4)java中定位资源位置有两种方式: Class和ClassLoader方式,底层实际上都是通过ClassLoader去定位资源的。
2.3 classLoader的getResource()和getResources()
@Test
public void testLoaderResources() throws IOException {
// 返回的是一个url
URL url = LoaderUtils.class.getClassLoader().getResource("test.loader");
System.out.println(url);
// 返回的是一个url集合
Enumeration<URL> urls = LoaderUtils.class.getClassLoader().getResources("test.loader");
System.out.println(urls.nextElement());
}
运行:
file:/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
file:/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
(1)classLoader提供了getResource()方法返回classpath下匹配到的第一个目标资源;也提供了getResources()方法返回classpath下匹配到的所有目标资源。而class不提供getResources()方式。
(2)classLoader.getResource():从classpath下返回目标资源,只返回匹配到的第一个目标资源,当前工程中的classpath下的文件优先被匹配返回。即classLoader.getResource()获取classpath下匹配到的第一个目标资源。
(3)classLoader.getResources():从classpath下返回目标资源,所有匹配到的目标资源都被返回。
比如当前工程中有test.loader文件,其他引入的jar包的classpath下也有test.loader文件,它会返回所有匹配到的资源。
我们可以看到它返回的是一个url集合。
classLoader.getResources()获取classpath下匹配到的所有目标资源,这正是springboot中如何能够读取所有jar包classpath下的spring.factories文件的原因!!!
2.4 返回目标资源流
// class和classLoader的方式都提供直接返回目标文件流
@Test
public void testAsStream() {
InputStream inputStream = LoaderUtils.class.getResourceAsStream("test.loader");
inputStream = LoaderUtils.class.getClassLoader().getResourceAsStream("test.loader");
}
(1)class和classLoader都提供getResourceAsStream()的方式直接返回目标资源流。
这个特性特别重要,当我们把java工程打成jar包后,工程里面的资源文件就只能通过这种直接返回Stream的方式来处理.
如果直接尝试将getResource()返回的url转换成一个file,是会报错的,找不到文件,因为jar包里面的file说白了没有绝对的磁盘路径,我们通过class或者classLoader直接读取目标文件的Stream.这才是读取jar包中资源文件的正确姿势。
3. 如何获取目标资源的绝对路径?
3.1 传入绝对路径,返回绝对路径
// 直接传入绝对路径,返回一个绝对路径
public static String getPathFromAbsolutePath(String absolutePath) {
return absolutePath;
}
测试:
public static void main(String[] args) throws IOException, URISyntaxException {
System.out.println(getPathFromAbsolutePath("test.loader"));
}
运行:
test.loader
3.2 使用spring的FileSystemResourceLoader
使用spring的 FileSystemResourceLoader,传入绝对路径,返回绝对路径
public static String getPathFromAbsolutePathUseFileSystemResourceLoader(String absolutePath) throws IOException {
ResourceLoader resourceLoader = new FileSystemResourceLoader();
Resource resource = resourceLoader.getResource(absolutePath);
return resource.getURI().getPath();
}
测试:
System.out.println(getPathFromAbsolutePathUseFileSystemResourceLoader("C:\\D\\zhaoehcode-java\\springboot\\src\\main\\resources\\test.loader"));
运行:
/C:/D/zhaoehcode-java/springboot/src/main/resources/test.loader
3.3 使用classLoader
传入相对路径,使用classLoader返回绝对路径。
注意,我们这里说的相对路径都是指的相对于classPath的路径,因为class和classLoader返回的路径就是classPath的路径。
因此,这里传入的相对路径就是拼接后组成的绝对路径。
classLoader传入的相对路径不能带/ 。
public static String getPathFromRelativePathUseClassLoader(String relativePath) throws FileNotFoundException {
ClassLoader classLoader = FileManager.class.getClassLoader();
URL resource = classLoader.getResource(relativePath);
String absolutePathOfCurrentFile = resource.getFile();
return absolutePathOfCurrentFile;
}
测试:
// 相对路径不带/
System.out.println(getPathFromRelativePathUseClassLoader("test.loader"));
运行:
/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
classLoader还有另外一种方式:
// 传入相对路径,使用classLoader返回绝对路径
public static String getPathFromRelativePathUseClassLoaderAsUri(String relativePath) throws URISyntaxException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL resource = classLoader.getResource(relativePath);
URI uri = resource.toURI();
return uri.getPath();
}
测试:
System.out.println(getPathFromRelativePathUseClassLoaderAsUri("test.loader"));
运行:
/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
(1)通过当前线程对象获取classLoader。
(2)通过返回的Resource独享的toURI()方法将其转换为Uri后再获取其路径,而不是getFile()。
这一点特别重要,因为Resource并不都是File,它返回的有可能是文件,也有可能在jar包中,是个jar包中的资源。
当资源是jar包中的时,直接getFile()是不行的。
3.4 使用class
传入相对路径,使用class返回绝对路径
class传入的相对路径一定要带/ 。
public static String getPathFromRelativePathUseClass(String relativePath) {
URL resource = FileManager.class.getResource(relativePath);
String absolutePathOfCurrentFile = resource.getFile();
return absolutePathOfCurrentFile;
}
测试:
System.out.println(getPathFromRelativePathUseClass("/test.loader"));
运行:
/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
3.5 使用spring的DefaultResourceLoader
传入相对路径,使用spring的ResourceLoader返回绝对路径
public static String getPathFromRelativePathUseResourceLoaderAsFile(String relativePath) throws IOException {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource classPathResource = resourceLoader.getResource("classpath:" + relativePath);
return classPathResource.getURI().getPath();
}
测试:
System.out.println(getPathFromRelativePathUseResourceLoaderAsFile("test.loader"));
运行:
/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
上面的classpath:前缀表示必须的:
不带 classpath: 前缀
public static String getPathFromRelativePathUseResourceLoaderNotClasspathAsFile(String relativePath) throws IOException {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource classPathResource = resourceLoader.getResource(relativePath);
return classPathResource.getURI().getPath();
}
测试:
System.out.println(getPathFromRelativePathUseResourceLoaderNotClasspathAsFile("test.loader"));
运行:
/C:/D/gitsource/zhaoehcode-java/springboot/target/classes/test.loader
它的getResource()方法返回的Resource对象,是spring自己抽象出来的一个接口,来表示环境中的所有资源。
4. 如何根据路径获取一个File对象?
##4.1 直接传入一个磁盘的绝对路径文件,获取对应的file
package code08handlefile;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
// file工具,根据路径获取一个文件对象
public class FileManager {
// 直接传入绝对路径,返回一个file
public static File readFileFromAbsolutePath(String absolutePath) {
// 根据绝对路径,实例化一个file对象
File file = new File(absolutePath);
return file;
}
}
测试:
public static void main(String[] args) {
File file = readFileFromAbsolutePath("C:\\D\\gitsource\\zhaoehcode-java\\springboot\\src\\main\\resources\\test.loader");
System.out.println("绝对路径 new File:" + file.exists());
}
运行:
绝对路径 new File:true
可以看到,通过 new File()构造方法,传入一个绝对路径,返回了这个文件。
其实,有必要对File构造器再说明一下。
new File()方法,也允许传入一个相对路径:只不过 new File() 中传入的相对路径,始终是相对于当前工作目录的根目录的,而不是相对于classpath。
如果我们工程根目录是springboot文件夹,则它就是相对于springboot文件夹的。
如果我们的工程是一个子module,则它是相对于整个工程的根目录而言的。
file = readFileFromAbsolutePath("springboot\\src\\main\\resources\\test.loader");
System.out.println("相对路径 new File:" + file.exists());
运行:
相对路径 new File:true
我们传入了相对路径: springboot\src\main\resources\test.loader
这个路径就是相对于整个工程根目录而言的,就是工作空间根目录。
如果我们尝试直接传入相对于classpath的路径:
file = readFileFromAbsolutePath("\test.loader");
System.out.println("相对classpath路径 new File:" + file.exists());
运行:
相对classpath路径 new File:false
所以,切记,new File()接收的相对路径是相对于工作空间根目录的。
4.2 spring的 FileSystemResourceLoader
使用spring的 FileSystemResourceLoader,传入绝对路径,返回一个文件对象
public static File readFileFromAbsolutePathUseFileSystemResourceLoader(String absolutePath) throws IOException {
ResourceLoader resourceLoader = new FileSystemResourceLoader();
Resource resource = resourceLoader.getResource(absolutePath);
// 获取目标资源对应的磁盘文件
File file = resource.getFile();
return file;
}
测试:
file = readFileFromAbsolutePathUseFileSystemResourceLoader("C:\\D\\gitsource\\zhaoehcode-java\\springboot\\src\\main\\resources\\test.loader");
System.out.println("FileSystemResourceLoader 绝对路径:" + file.exists());
file = readFileFromAbsolutePathUseFileSystemResourceLoader("test.loader");
System.out.println("FileSystemResourceLoader 相对路径:" + file.exists());
运行:
FileSystemResourceLoader 绝对路径:true
FileSystemResourceLoader 相对路径:false
可以看到,spring的FileSystemResourceLoader就是从系统磁盘文件中加载文件路径,它只支持绝对路径。
4.3 使用jdk的classLoader
使用jdk提供的classLoader加载器进行加载
// 直接传入相对路径(相对resources classpath资源的路径)
public static File readFileFromRelativePathUseClassLoader(String relativePath) throws FileNotFoundException {
// 获取当前class loader
ClassLoader classLoader = FileManager.class.getClassLoader();
// 根据classLoader获取当前资源文件的资源
URL resource = classLoader.getResource(relativePath);
// 获取当前资源文件的绝对路径
String absolutePathOfCurrentFile = resource.getFile();
File file = new File(absolutePathOfCurrentFile);
return file;
}
测试:
file = readFileFromRelativePathUseClassLoader("test.loader");
System.out.println("classLoader 相对路径:" + file.exists());
运行:
classLoader 相对路径:true
同理,classLoader同样支持另外一种:
public static File readFileFromRelativePathUseClassLoaderAsUri(String relativePath) throws URISyntaxException {
// 通过线程的方式获取当前class loader,和上面获取class loader的方式差不多
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 根据classLoader获取当前资源文件的资源
URL resource = classLoader.getResource(relativePath);
URI uri = resource.toURI();
File file = new File(uri);
return file;
}
这种方式和上面的差不多。
当然,通过获取路径的案例可知,jdk必然也支持通过class对象去返回一个File的方法。
4.4 spring的DefaultResourceLoader
使用spring提供的resourceLoader进行加载,底层还是使用的jdk的ClassLoader
// 直接传入相对路径(相对resources资源的路径)
public static File readFileFromRelativePathUseResourceLoaderAsFile(String relativePath) throws IOException {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource classPathResource = resourceLoader.getResource("classpath:" + relativePath);
// 获取目标资源对应的磁盘文件
File file = classPathResource.getFile();
return file;
}
classpath: 不是必须的。
// 直接传入相对路径(相对resources资源的路径)
// 使用spring提供的resourceLoader进行加载,底层还是使用的jdk的ClassLoader
public static File readFileFromRelativePathUseResourceLoaderAsResourceFile(String relativePath) throws IOException {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource(relativePath);
// 获取目标资源对应的磁盘文件
File file = resource.getFile();
return file;
}
测试:
file = readFileFromRelativePathUseResourceLoaderAsFile("test.loader");
System.out.println("DefaultResourceLoader 相对路径(带classpath:):" + file.exists());
file = readFileFromRelativePathUseResourceLoaderAsResourceFile("test.loader");
System.out.println("DefaultResourceLoader 相对路径(不带classpath:):" + file.exists());
运行:
DefaultResourceLoader 相对路径(带classpath:):true
DefaultResourceLoader 相对路径(不带classpath:):true
总结:
(1)各种方式返回一个File,要么是返回的对象中能够直接获取到file,要么是能够直接获取到路径,要么是能获取到URI,反正最终都能返回一个File对象回来。
(2)上述各种方式,都仅仅局限于从当前磁盘目录上返回一个File,举一个例子,当我们把服务打成jar包,上面的方式都无法读取jar包中指定的文件。
因为,jar包中的文件已经不是一个磁盘文件了,它返回的URI是一个jar类型的,我们没法直接将它的路径转换为File,或者直接从它的资源定位对象中获取File.
其实,我们要读取jar包中的某个文件,说白了就是读取它的内容,我们不必非要获取它的File对象,只要能够获取到目标资源的InputStream即可。
5. 读取文件内容
前面分析了,如果目标文件在磁盘文件系统中,那么我们可以获取到目标文件的File对象,然后通过FileInputStream(file)去创建输入流,来获取其中的数据。
5.1 Java中最原始读取文件内容的方式
public static void main(String[] args) throws IOException {
File file = new File("C:\\D\\gitsource\\zhaoehcode-java\\springboot\\src\\main\\resources\\test.loader");
if (file.exists()) {
InputStream inputStream = new FileInputStream(file);
StringBuffer buffer = new StringBuffer();
int ch;
// 循环读取数据到int类型中,实际上读取的是字节
while ((ch = inputStream.read()) != -1) {
// 将字节转换为char后拼接为字符串
buffer.append((char) ch);
}
inputStream.close();
System.out.println("content is:" + buffer.toString());
}
}
运行:
content is:ææ¯èµµäºèï¼
ä½ æ¯è°åï¼
发现是乱码。
因为我们的文件中存储的是中文,简单点说是字符,对于字符,要使用字符流来读取,InputStream读取的是二进制流。
public static void main(String[] args) throws IOException {
File file = new File("C:\\D\\gitsource\\zhaoehcode-java\\springboot\\src\\main\\resources\\test.loader");
if (file.exists()) {
InputStream inputStream = new FileInputStream(file);
// 使用reader字符流包装inputStream
Reader reader = new InputStreamReader(inputStream);
StringBuffer buffer = new StringBuffer();
int ch;
// 循环读取数据到int类型中,实际上读取的是字节
while ((ch = reader.read()) != -1) {
// 将字节转换为char后拼接为字符串
buffer.append((char) ch);
}
inputStream.close();
reader.close();
System.out.println("content is:" + buffer.toString());
}
}
运行:
content is:我是赵二虎!
你是谁啊!
其实后面使用开源工具读取文件内容,其底层都是对jdk中IO流的包装。
5.2 通过new FileInputStream的方式
new FileInputStream()的方式接收一个File对象,直接将file对象转换为Stream。
前提是这个File对象要存在,如果在jar包中,file都不存在,这种方式就会报错!!!
结合上面封装的工具,整体编写案例如下:
// 直接传入绝对路径
public static InputStream readStreamFromAbsolutePath(String absolutePath) throws FileNotFoundException {
// 根据绝对路径,实例化一个file对象
File file = FileManager.readFileFromAbsolutePath(absolutePath);
// 将磁盘上的file文件转换为输入流并返回
return new FileInputStream(file);
}
// 直接传入相对路径(相对resources资源的路径)
// 使用spring的 FileSystemResourceLoader
public static InputStream readStreamFromRelativePathUseResourceLoaderAsFileSystemResource(String relativePath) throws IOException {
File file = FileManager.readFileFromAbsolutePathUseFileSystemResourceLoader(relativePath);
return new FileInputStream(file);
}
// 直接传入相对路径(相对resources资源的路径)
public static InputStream readStreamFromRelativePathUseClassLoader(String relativePath) throws FileNotFoundException {
File file = FileManager.readFileFromRelativePathUseClassLoader(relativePath);
return new FileInputStream(file);
}
// 直接传入相对路径(相对resources资源的路径)
public static InputStream readStreamFromRelativePathUseClassLoaderAsUri(String relativePath) throws FileNotFoundException, URISyntaxException {
File file = FileManager.readFileFromRelativePathUseClassLoaderAsUri(relativePath);
return new FileInputStream(file);
}
// 直接传入相对路径(相对resources资源的路径)
public static InputStream readStreamFromRelativePathUseResourceLoaderAsFile(String relativePath) throws IOException {
File file = FileManager.readFileFromRelativePathUseResourceLoaderAsFile(relativePath);
return new FileInputStream(file);
}
// 直接传入相对路径(相对resources资源的路径)
// 使用spring提供的resourceLoader进行加载,底层还是使用的jdk的ClassLoader
public static InputStream readStreamFromRelativePathUseResourceLoaderAsResourceFile(String relativePath) throws IOException {
File file = FileManager.readFileFromRelativePathUseResourceLoaderAsResourceFile(relativePath);
return new FileInputStream(file);
}
5.3 不通过File对象转换,直接获取流
这种方式就适用于读取jar包中的文件内容,直接读取的就是Stream,而不需要感知它的File对象。
// 直接传入相对路径(相对resources资源的路径)
// 使用jdk提供的classLoader加载器进行加载
public static InputStream readStreamFromRelativePathUseClassLoaderAsOpenStream(String relativePath) throws IOException {
// 获取当前class loader
ClassLoader classLoader = FileManager.class.getClassLoader();
// 根据classLoader获取当前资源文件的资源
URL resource = classLoader.getResource(relativePath);
// url的openStream直接返回流
return resource.openStream();
}
// 直接传入相对路径(相对resources资源的路径)
// 使用jdk提供的classLoader加载器进行加载
public static InputStream readStreamFromRelativePathUseClassLoaderAsStream(String relativePath) {
// 获取当前class loader
ClassLoader classLoader = FileManager.class.getClassLoader();
// 直接通过classLoader返回目标资源流,classLoader直接返回目标资源流的方式可以解决jar包中资源文件读取不到的问题
return classLoader.getResourceAsStream(relativePath);
}
// 直接传入相对路径(相对resources资源的路径)
// 使用spring提供的resourceLoader进行加载,底层还是使用的jdk的ClassLoader
public static InputStream readStreamFromRelativePathUseResourceLoader(String relativePath) throws IOException {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource classPathResource = resourceLoader.getResource("classpath:" + relativePath);
InputStream inputStream = classPathResource.getInputStream();
return inputStream;
}
// 上面的classpath: 不是必须的。
public static InputStream readStreamFromRelativePathUseResourceLoaderAsResourceStream(String relativePath) throws IOException {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource(relativePath);
return resource.getInputStream();
}
// 直接传入相对路径(相对resources资源的路径)
// 使用spring的 FileSystemResourceLoader
public static InputStream readStreamFromRelativePathUseResourceLoaderAsFileSystemResourceStream(String relativePath) throws IOException {
ResourceLoader resourceLoader = new FileSystemResourceLoader();
Resource resource = resourceLoader.getResource(relativePath);
return resource.getInputStream();
}
总结:
读取文件内容,其根本目的是要获取到目标文件的输入流信息。
(1)第一种方式:获取到磁盘上文件的路径,拿到File对象,然后将File包装成文件流即可。
(2)第二种方式:直接从根本上解决问题,获取目标资源的URL对象,将URL对象转换成流。
java读取文件,我们首先要拿到Stream,然后再看是什么文件,再做对应处理。
比如json文件,xml文件,properties文件,yml文件等,都有各自的解析方式,但是,最根本的是首先需要将文件读取到InputStream中,才能做后续的动作。
6. 向目标文件写入数据
向文件写入数据,那么就要求我们的目标文件肯定是在磁盘目录上,而不能是在jar包里。
如果在jar包或者其他压缩包,需要做特殊处理,先解压,然后写数据,再重新打包。
所以,它的本质还是直接先磁盘文件系统中的某个文件写入数据。
(1)获取到目标文件路径(绝对路径或者相对路径)。
(2)根据路径获取到目标文件File对象(直接new File(),或者是其他对象间接的返回File实例)。 如果读取jar包里的某个文件,那么强制获取它的File对象是会报错的。
(3)判断目标file是否存在,如果存在,则通过OutputStream流写入数据;否则,不写入(当然也可以递归的创建目标文件以及父目录后再写入)。
请看我的这篇文章去学习如何创建文件、以及向目标文件写入数据。
这里就不再赘述了。
5. java读取json文件
下面看看具体如何读取Json文件。
读取json文件说白了就是要把文件流或者直接把文件转换成json字符串,要使用到上面封装的两个类。
(1)使用纯java的方式将stream转换为json字符串
public static String readJsonFile(String filePath) {
String jsonStr = "";
try {
InputStream inputStream = StreamManager.readStreamFromRelativePathUseClassLoader(filePath);
// 根据文件绝对路径创建一个File对象
// 根据文件对象包装一个Reader流
Reader reader = new InputStreamReader(inputStream, "utf-8");
int ch;
// 创建一个StringBuffer对象
StringBuffer stringBuffer = new StringBuffer();
// 从reader中遍历读取数据,然后拼接到StringBuffer中
while ((ch = reader.read()) != -1) {
stringBuffer.append((char) ch);
}
reader.close();
jsonStr = stringBuffer.toString();
return jsonStr;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
(2)使用apache common的方式将stream转换json字符串
public static String readJsonFromFile(String fileName) throws IOException {
InputStream inputStream = StreamManager.readStreamFromRelativePathUseResourceLoader(fileName);
// 使用apache common的公共包方便地将inputStream转换为String
return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
}
(3)使用apache common包的FileUtils将file转换为json字符串
public static String loadFileFromPath(String filePath) throws IOException {
File file = FileManager.readFileFromRelativePathUseResourceLoaderAsResourceFile(filePath);
return FileUtils.readFileToString(file, StandardCharsets.UTF_8);
}
(4)使用apache common包的FileUtils,多转了几手将file转换为json字符串
public static String readJsonToBeString(String filePath) throws IOException {
FileInputStream fileInputStream = null;
String fileContent = "";
File file = FileManager.readFileFromRelativePathUseResourceLoaderAsResourceFile(filePath);
try {
fileInputStream = FileUtils.openInputStream(file);
fileContent = IOUtils.toString(fileInputStream, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return fileContent;
}
(5)使用nio将file转换为json字符串
public static String readFileContent(String filePath) {
try {
File file = FileManager.readFileFromRelativePathUseClassLoaderAsUri(filePath);
return new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8.toString());
} catch (URISyntaxException | IOException e) {
return null;
}
}
(6)使用jackson将file转换为json字符串
public static String readFileAsJsonString(String filePath) throws IOException {
File file = FileManager.readFileFromRelativePathUseClassLoader(filePath);
ObjectMapper objectMapper = new ObjectMapper();
Map jsonMap = objectMapper.readValue(file, Map.class);
String jsonString = objectMapper.writeValueAsString(jsonMap);
return jsonString;
}
文档信息
- 本文作者:Marshall