Java读取资料文件相关类方法


前言

学习过程中一些不知道的类和方法的记录


一、Spring之ClassPathResource

ClassPathResource是用于Spring中加载资源文件的类

1.引入库

代码如下(示例):

import org.springframework.core.io.ClassPathResource;

2.读取资源文件

代码如下(示例):

@Test
public void testClassPathResource() throws IOException {
     Resource res = new ClassPathResource("resource/ApplicationContext.xml");
     InputStream input = res.getInputStream();
     Assert.assertNotNull(input);
}

3.源码解读

内部源码:

public ClassPathResource(String path) {
     this(path, (ClassLoader) null);
 }
public ClassPathResource(String path, ClassLoader classLoader) {
    Assert.notNull(path, "Path must not be null");
    String pathToUse = StringUtils.cleanPath(path);
    if (pathToUse.startsWith("/")) {
        pathToUse = pathToUse.substring(1);
    }
    this.path = pathToUse;
    this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
public ClassPathResource(String path, Class<?> clazz) {
     Assert.notNull(path, "Path must not be null");
     this.path = StringUtils.cleanPath(path);
     this.clazz = clazz;
}

/**
 * 读取资源文件
 * This implementation opens an InputStream for the given class path resource.
 * @see java.lang.ClassLoader#getResourceAsStream(String)
 * @see java.lang.Class#getResourceAsStream(String)
 */
@Override
public InputStream getInputStream() throws IOException {
    InputStream is;
    if (this.clazz != null) {
        is = this.clazz.getResourceAsStream(this.path);
    }
    else if (this.classLoader != null) {
        is = this.classLoader.getResourceAsStream(this.path);
    }
    else {
        is = ClassLoader.getSystemResourceAsStream(this.path);
    }
    if (is == null) {
        throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
    }
    return is;
}

该类获取资源文件有两种方法:通过class获取和classLoader获取

@Test
public void testResouce() {
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    System.out.println(loader.getResource("").getPath());

    System.out.println(this.getClass().getResource("").getPath());
    System.out.println(this.getClass().getResource("/").getPath());

    System.out.println(System.getProperty("user.dir"));
}

输出结果:

/home/sunny/workspace/spring-01/target/test-classes/
/home/sunny/workspace/spring-01/target/test-classes/com/me/spring/spring_01/
/home/sunny/workspace/spring-01/target/test-classes/
/home/sunny/workspace/spring-01

Class.getResource("")获取的是相对于当前类的相对路径

Class.getResource("/")获取的是classpath的根路径

ClassLoader.getResource("")获取的是classpath的根路径

在创建ClassPathResource对象时,我们可以指定是按Class的相对路径获取文件还是按ClassLoader来获取。


二、FileUtils

1.文件操作工具类

官方API文档:http://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/FileUtils.html

maven依赖:

<dependency>
       <groupId>commons-io</groupId>
       <artifactId>commons-io</artifactId>
       <version>2.8.0</version>
</dependency>

2.方法使用

一、拷贝文件

static voidcopyFile(File srcFile, File destFile)
static voidcopyFile(File srcFile, File destFile, boolean preserveFileDate)
static longcopyFile(File input, OutputStream output)
static voidcopyFileToDirectory(File srcFile, File destDir)

1)拷贝文件到文件: copyFile(File srcFile, File destFile)

File file = new File("E:\\java\\file01\\abc雪.jpg");
	String destFilePath = "E:\\java\\file02";
	String destFileName = "abc雪02.jpg";
	try {
		FileUtils fileUtils = new FileUtils();
		//文件拷贝到新的位置并保存文件的日期。
		fileUtils.copyFile(file, new File(destFilePath,destFileName));
		System.out.println("文件拷贝成功");
	} catch (IOException e) {
		e.printStackTrace();
		System.out.println(e.getMessage());
	}

2)拷贝文件到字节输出流。:copyFile(File input, OutputStream output)

String destFileName = "abc雪03.jpg";
//从文件copy to an字节输出流。
FileUtils.copyFile(file, new FileOutputStream(new File(destFilePath,destFileName)));

3)拷贝文件到文件的目录保存文件的日期:copyFileToDirectory(File srcFile, File destDir)

拷贝的文件名无法自定义,和原文件名一样

//拷贝文件到文件的目录保持文件的日期。
FileUtils.copyFileToDirectory(file, new File(destFilePath));

二、拷贝目录及文件

static voidcopyDirectory(File srcDir, File destDir)
static voidcopyDirectory(File srcDir, File destDir, boolean preserveFileDate)
static voidcopyDirectory(File srcDir, File destDir, FileFilter filter)
static voidcopyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate)

1)将整个目录拷贝到新位置,并保持原文件日期:copyDirectory(File srcDir, File destDir)

其包含文件及子目录文件并保持原文件日期

File file = new File("E:\\java\\file01");
		String destFilePath = "E:\\java\\file03";
		try {
			//将整个目录复制新位置,并保持原文件日期。
			FileUtils.copyDirectory(file, new File(destFilePath));
			System.out.println("文件目录拷贝成功");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println(e.getMessage());
		}

2)将已筛选的目录拷贝的新位置:copyDirectory(File srcDir, File destDir, FileFilter filter)

文件过滤器筛选 其包含文件及子目录文件 拷贝,并保持原文件日期。

String destFilePath = "E:\\java\\file04";
//将已筛选的目录复制,并保持原文件日期的新位置。
FileUtils.copyDirectory(file, new File(destFilePath), new FileFilter() {
	@Override
	public boolean accept(File pathname) {
		if(pathname.isDirectory()) return true;
		else {
			boolean b1 = pathname.getName().endsWith(".txt");
			boolean b2 = pathname.getName().endsWith(".jpg");
			return b1 || b2;
		}
	}
});

三、删除目录及文件

static voiddeleteDirectory(File directory)
static booleandeleteQuietly(File file)
static voidforceDelete(File file)

1)删除指定文件,从不引发异常:deleteQuietly(File file)

File file = new File("E:\\java\\file04\\abc雪.jpg");
//删除指定文件,从不引发异常。
FileUtils.deleteQuietly(file);

2)删除指定文件,不存在报异常:forceDelete(File file)

File file = new File("E:\\java\\file04\\abc雪.jpg");
		try {
			FileUtils.forceDelete(file);
			System.out.println("操作成功");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println(e.getMessage());
		}

3)递归删除目录:deleteDirectory(File directory)

删除其包含文件及子目录文件

File file = new File("E:\\java\\file04\\abc雪.jpg");
		//递归删除目录。
		try {
			FileUtils.deleteDirectory(new File(destFilePath));
			System.out.println("操作成功");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println(e.getMessage());
		}

四、清除目录:cleanDirectory(File directory)

static void	cleanDirectory(File directory)
Cleans a directory without deleting it.

清除该目录下的文件及子目录文件而不删除该目录文件夹。该目录不存在会报错

String destFilePath = "E:\\java\\file04";
try {
	FileUtils.cleanDirectory(new File(destFilePath));
	System.out.println("操作成功");
} catch (IOException e) {
	e.printStackTrace();
	System.out.println(e.getMessage());
}

五、将字符串写入文件中,writeStringToFile
writeStringToFile一共有四个参数

  1. file (File) : file 指定数据存放在什么文件下,会默认创建文件夹和文件
  2. data (String):指定写入的数据
  3. encoding (String):指定写入时编码格式 (参数可选)
  4. append (boolean):指定插入时是覆盖插入还是追加插入 默认为false false是覆盖插入 true为追加插入 (参数可选)
 String writeData="FileUtils test";
        try {
            FileUtils.writeStringToFile(new File("e:/file/fileTest.txt"),writeData,"UTF-8",false);
            System.out.println("写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        }

六、将指定的文件夹中的内容读取出来以String的形式来显示:readFileToString

readFileToString 二个参数

  1. file (File) : file 指定数据存放在什么文件下,会默认创建文件夹和文件
  2. encoding (String):指定写入时编码格式 (参数可选)
   public static void main(String[] args) {

        String  readData=null;
        try {
            readData= FileUtils.readFileToString(new File("e:/file/fileTest.txt"),"UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("读取的数据为:"+readData);

    }

三、TypeReference

1.获取泛型参数

对进行泛型的反序列化,使用TypeReference可以明确的指定反序列化的类型,
代码如下(示例):

//后台java代码 String planJson = request.getParameter("planJson");
 if (!Tools.isEmpty(planJson)) {
      Plan plan = JSON.parseObject(planJson, new TypeReference<Plan>() {
      });
      modelMap.addAttribute("plan", plan);
 }

JSON.parseObject(planJson, new TypeReference<Plan>(){})是把字符串planjson转化为相应的JSONObject对象,“键值对”形式,然后通过new TypeReference<Plan>(){ }匿名内部类来吧planjson的JSONObject转化为Plan对象,注意一点,前台在做 $("#addForm").serializeJson() 处理的时候,form表单里还有许多隐藏域,不属于plan对象本身的属性,通过TypeReference只把属于plan对象属性的参数和值组装成plan对象


四、ClassPathResource、FileUtils和TypeReference

读取路径中的json文件并保存

代码如下:

private ConcurrentHashMap<String, List<Info>> cache;
private ClassPathResource resource;
public void load(String filename) {
        // 从文件中加载缓存到map中
        try {
            //获得资源文件
            resource = new ClassPathResource(filename);
            //读取资源文件
            String str2Cache = FileUtils.readFileToString(resource.getFile(), StandardCharsets.UTF_8);
            cache = new ConcurrentHashMap<>();
            //将文件内容保存在cache这个currentmap中。
            cache = JSON.parseObject(str2Cache, new TypeReference<ConcurrentHashMap<String,List<Info>>>(){});
        } catch (IOException e) {
           log.error("从文件中加载Cache失败.{}", e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }
private void save() {
        // 将map中的数据写入到文件中
        try {
            String cache2String = JSON.toJSONString(cache);
            FileUtils.writeStringToFile(resource.getFile(), cache2String, StandardCharsets.UTF_8);
        } catch (IOException e) {
            log.error("写入文件失败.{}", e.getMessage());
        }
    }

五、BufferedReader

1.接收控制台信息

代码如下:

BufferedReader input = new BufferedReader(new InputStreamReader(System.in));

2.读取文件

代码如下:

BufferedReader in  = new BufferedReadernew FileReader(“foo.in”));

3.BufferedReader读取资源文件

代码如下:

 //读文件
    public static String readFiles(String fileName) {
        String jsonstr = "";
        File jsonfile = new File(fileName);
        BufferedReader reader = null;
        if (!jsonfile.exists()) {
            System.out.println("not found "+fileName);
            return null;
        }
        try {
            reader = new BufferedReader(new FileReader(jsonfile));
            String readstr = null;
            int line = 1;
            while ((readstr = reader.readLine()) != null) {
                jsonstr += readstr;
                line++;
            }
            return jsonstr;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

六.BufferedWriter

1.API

(1) 写一个字符到字符缓冲区中

  /**
     * 写入一个字符,并存入到字符缓冲区中
     *
     * @exception  IOException  If an I/O error occurs
     */
    public void write(int c) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar >= nChars)
                flushBuffer();
            cb[nextChar++] = (char) c;  //将单个字符存入到
        }
    }

(2)写一个字符数组的一部分

  * 写一个字符数组的一部分
    *
     */
    public void write(char cbuf[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return;
            }
 
            if (len >= nChars) {
                //如果请求长度超过输出缓冲区的大小则直接进行输出,而不用字符缓冲区做缓冲
                flushBuffer();
                out.write(cbuf, off, len);  //实际是调用的OutputStreamWriter的write方法本质是调用StreamEncoder的write方法执行的
                return;
            }
 
            int b = off, t = off + len;
             
             //此循环的目的就是保证len个字符务必被复制到字符缓冲区中区
                while (b < t) {      
                int d = min(nChars - nextChar, t - b);  //判断字符缓冲区中的剩余位置和要写入的字符长度最小值,若字符缓冲区空间充足,则复制len个字符,若不充足,则复制剩余位置个字符
                System.arraycopy(cbuf, b, cb, nextChar, d); //从目标数组cbuf的下标b中复制d个字符到字符缓冲区
                b += d;
                nextChar += d;
                if (nextChar >= nChars) //判断字符缓冲区是否已满?若满了,则调用flushBuffer方法把缓冲区中的内容全部输出,然后按道理此时字符缓冲区应该就是空了的?
                    flushBuffer();
            }
        }
    }

源码分析:
本质是调用OutputStreamWriter的write(cbuf, off, len)方法而OutputStreamWriter的write(cbuf, off, len)方法实际是调用StreamEncoder的write方法执行的

 1、传入字符数组cbuf,字符数组的偏移点off,以及要写入的字符个数len--代表要从字符数组cbuf中下标off开始写入len个字符

 2、 前期条件判断避免出现RuntimeException异常所以必须抛出

 3、 当要写入的字符个数len大于字符缓冲区的长度时则意味着就算字符缓冲区cb是空的也无法载入写入字符,那么就没必有调用字符缓冲区了,直接调用write(cbuf, off, len)方法,out变量是OutputStreamWriter的对象,而OutputStreamWriter内的方法都是StreamEncoder类的方法执行。因此实际调用的是StreamEncoder的write方法。在StreamEncoder中直接把字符数组cbuf通过编码器编码到StreamEncoder的字节缓冲区中

4、当要写入的字符个数len小于字符缓冲区的长度时,通过 System.arraycopy的方法把字符数组cbuf内要写入的字符复制到字符缓冲区cb中,其中while循环的作用就是保证符数组cbuf内要写入的字符全部复制到字符缓冲区cb中

2.写资源文件

代码如下(示例):

//写文件
    public static void writeFiles(String content, String fileName) {
        BufferedWriter bufferedWriter = null;
        try {
            File file = new File(fileName);
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fileWriter = new FileWriter(fileName, true);
            bufferedWriter = new BufferedWriter(fileWriter);
            bufferedWriter.write(content);
            //newLine(),换行
            //bufferedWriter.newLine();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bufferedWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

3.BufferedWriter流与OutputStreamWriter的关系

1)前面OutputStreamWriter的API说明中提到过:为了获得最高效率,请考虑在BufferedWriter中包装OutputStreamWriter,以避免频繁的转换器调用这句话,理解如下:
当我们直接使用OutputStreamWriter进行输出时,不管是一个字符,还是一个数组,都会调用字符编码器进行转换,因为其是直接调用的StreamEncoder的implWrite方法,若存在for循环100次,每次调用write(int c)方法写入一个字符,那么最终就会调用100次的implWrite方法,就会调用100次的字符编码器进行转换
但是当我们使用BufferedWriter包装OutputStreamWriter时,每次调用write(int c)方法时,都只是将字符写入到BufferedWriter类的字符缓冲区中。到最后调用flush方法或者close方法时,才会调用一次StreamEncoder的implWrite方法,也就是调用一次字符编码器
因此BufferedWriter可以提高字符输出的效率,提高的关键点在于:存在字符缓冲区用于存储字符


版权声明:本文为hjukyjhg56原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。