java基础-文件与NIO

文件操作

工作目录

@Test
public void testFilePath (){
    //项目的工作路径
    System.out.println(System.getProperty("user.dir"));
    //绝对路径,window下是D:loglog.txt
    File file = new File("\log\log.txt");
    System.out.println(file.getAbsolutePath());
    // 相对于工作路径
    File file2 = new File("log\log.txt");
    System.out.println(file2.getAbsolutePath());
}

当前工作目录,可以:

  1. 启动参数设置:如-Duser.dir=C: est指定
  2. 默认是java -jar 当前目录
  3. 开发过程中在idea里面设置:edit configuration》working directory

文件夹创建

@Test
public void testMkdir () throws IOException {
    File file1 = new File("log/log");
    File file2 = new File("test/log");
    System.out.println(file1.isDirectory());//false
    System.out.println(file1.isFile());//false
    System.out.println(file1.mkdirs());//true
    System.out.println(file2.mkdir());//false
    System.out.println(file1.isDirectory());//true
    System.out.println(file1.isFile());//false
}

mkdir只有在父目录存在时才会创建新的文件夹,并返回true,如果父目录不存在或者文件夹已经存在返会false

mkdirs当父目录不存在时会把父目录也一起创建,如果文件夹已经存在则返回false

@Test
public void testRandomAccessFile2 () throws FileNotFoundException {
    //当上级路径存在时会自动创建,否则报FileNotFoundException
    final RandomAccessFile fileOutputStream = new RandomAccessFile("log/fa.txt", "rw");
}

当上级路径存在时会自动创建,否则报FileNotFoundException

删除

@Test
public void testDelete (){
    File parentFile = new File("log/log");
    parentFile.mkdirs();
    File file1 = new File("log/log/1.txt");
    try {
        System.out.println(file1.createNewFile());//true
        //目录下由文件无法删除
        System.out.println(parentFile.delete());//false
        //删除该目录下所有文件
        System.out.println(file1.delete());//true
        //该目录才可以删除
        System.out.println(parentFile.delete());//true
        //退出jvm时删除文件
        file1.deleteOnExit();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

删除文件夹时,必须保证文件夹时空的

文件属性

@Test
public void testFileProperty (){
    File file1 = new File("log/1.txt");
    try {
        boolean newFile = file1.createNewFile();
        if(newFile || file1.exists()){
            System.out.println(file1.canExecute());
            System.out.println(file1.canRead());
            System.out.println(file1.canWrite());
            //是否隐藏
            System.out.println(file1.isHidden());
            //最后修改时间
            System.out.println(FgDateUtils.millsToDateTime(file1.lastModified()));
            //文件大小 字节为单位
            System.out.println(file1.length());
        }
    } catch (IOException e) {

    }
}

列出文件和目录

@Test
public void testListFile (){
  //列出根目录,window下时C:\、d:\、e:
  File[] files = File.listRoots();
    Arrays.stream(files).forEach(System.out::println);
    File file = new File("d:\");
    //列出指定文件下的所有文件和文件夹
  Arrays.stream(Objects.requireNonNull(file.listFiles()))
  .forEach(n-> {
      System.out.println(n.getName()+ (n.isDirectory()?
                                        "(directory)": "(file)"));
      if(n.isFile()){
          StringBuilder result = createFileMd5(n);
          System.out.println(result);
      }
  });
    //指定file过滤
  System.out.println(Arrays.stream(Objects.requireNonNull(file.listFiles(File::isFile))).count());
}
private StringBuilder createFileMd5(File n) {
    StringBuilder result = new StringBuilder();
    try (FileInputStream fileInputStream = new FileInputStream(n)){
        final byte[] bytes = fileInputStream.readAllBytes();
        final MessageDigest md5 = MessageDigest.getInstance("MD5");
        md5.update(bytes);
        final byte[] digest = md5.digest();
        for (int i = 0; i < digest.length; i++) {
            result.append(Integer.toString((digest[i] & 0xff) + 0x100, 16)
                          .substring(1));
        }
    } catch (IOException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return result;
}

BufferedInputStream和FileInputStream

@Before
public void createFile() throws IOException {
    final File file = new File("log/1.txt");
    if (file.createNewFile()) {
        final FileOutputStream fileOutputStream = new FileOutputStream(file);

        for (int i = 0; i < 1000_000; i++) {
            fileOutputStream.write(str.getBytes());
        }
        fileOutputStream.close();
        System.out.println("创建文件"+file.length() /1000/1000d);
    }
}
@Test
public void testBufferedStream (){
    //buffer:148/103、fileinputStream:128/90、randomAccess:109/83,,map:11
    int size= 8192;
    //buffer:174/116,fileinputStream:959/411、randomaccess:724/558
    //int size= 1024;
    final long begin = System.currentTimeMillis();
    try(final BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("log/1.txt"))){
        final byte[] bytes = new byte[size];
        while (inputStream.read(bytes) != -1) {
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    System.out.println("BufferedInputStream time:"+(System.currentTimeMillis() - begin));

    final long begin1 = System.currentTimeMillis();
    try(final FileInputStream inputStream = new FileInputStream("log/1.txt")){
        final byte[] bytes = new byte[size];
        while (inputStream.read(bytes) != -1) {
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    System.out.println("time:"+(System.currentTimeMillis() - begin1));

    final long begin2 = System.currentTimeMillis();
    try (RandomAccessFile file = new RandomAccessFile("log/1.txt", "rw");){
        final byte[] bytes = new byte[size];
        while(file.read(bytes) !=-1){
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    System.out.println("time:"+(System.currentTimeMillis() - begin2));

    final long begin3 = System.currentTimeMillis();
    final File fileDes = new File("log/1.txt");
    try (RandomAccessFile file = new RandomAccessFile(fileDes, "rw");){
        final MappedByteBuffer map = file.getChannel().map(MapMode.READ_ONLY, 0, fileDes.length());
        //final CharBuffer decode = Charset.forName("UTF-8").decode(map);
        //System.out.println(decode);
        map.clear();
        //false ,并没有载入内存
        System.out.println(map.isLoaded());
    } catch (IOException e) {
        e.printStackTrace();
    }

    System.gc();
    System.out.println("time:"+(System.currentTimeMillis() - begin3));

}

文件大小约为160M

BufferedInputStream内部维护了一个byte数组,默认大小为8192。所以在read(bytes),bytes很小时,BufferedInputStream效率很高,应该数据其实都在BufferedInputStream内部,不需要读取磁盘,

而FileInputStream 每次 read 都需要读取磁盘,所以效率慢.但是如果读取的byte[] bytes的大小和8192差不多时,效率差不多甚至更快

MappedByteBuffer使用内存映射,时间会11ms左右,非常快。

RandomAccessFile

RandomAccessFile没有实现InputStream或者OutputStream,但同时能读写文件。此外RandomAccessFile包含一个file-pointer指针指向下一个文件读写的地方。

long getFilePointer( );//返回文件记录指针的当前位置
void seek(long pos );//将文件指针定位到pos位置

RandomAccessFile的读写模式:

**"r" : ** 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw": 打开以便读取和写入。
"rws": 打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。
"rwd" : 打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。

由于RandomAccess可以随机访问文件任意位置,所以可以用于文件断点续传。客户端上传额外的参数告诉服务器,从哪里开始传。

@Test
public void testRandomAccess (){
    try {
        final RandomAccessFile file = new RandomAccessFile("log/1.txt","rw");
        file.seek(str.getBytes().length);
        //这里的readline也是一个字节一个字节读,所以效率会非常慢
        final String s = file.readLine();
        ////反编码回文件中本原始的字节流
        final byte[] rawBytes = s.getBytes(StandardCharsets.ISO_8859_1);
        // //String 构造函数默认接受 UTF-8 编码
        System.out.println(new String(rawBytes));
        //此时文件指针指向读取的最新位置
        System.out.println(file.getFilePointer());
        file.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

FileChannel

UML

image-20210423142405157

从FileChannel的继承关系以及各个接口的方法也能知道相应的功能和作用:

  • Channel:代表打开了一个实体如硬件设备、文件、网络socket等IO终端的连接。只声明了最简单的isOpen判断连接是否打开,和close关闭连接
  • ReadableByteChannel:只声明了一个read(ByteBuffer)方法,表示可以从当前Channel读取数据到ByteBuffer中。同一时刻,在同一个readable channel 上,只允许一个读操作正在执行,其他类型的io操作能否同时进行取决于Channel类型。
  • WritableByteChannel:只声明了一个write(ByteBuffer)方法,表示可以将ByteBuffer的数据写入当前Channel。同一时刻,在同一个writable channel 上,只允许一个写操作正在执行,其他类型的io操作能否同时进行取决于Channel类型。
  • ScatteringByteChannel,声明了read(ByteBuffer[] dsts, int offset, int length)方法,可以在一次调用,读取数据到多个ByteBuffer中。如在网络协议(network protocols)定义消息为片段(定长的header和不定长的body),就可以使用ScatteringByteChannel将消息读取到多个buffer中
  • GatheringByteChannel:声明了write(ByteBuffer[] srcs, int offset, int length)方法,可以在以调用中,将多个ByteBuffer写入Channel
  • SeekableByteChannel:维护position指针执行当前操作位置,并且可以被修改position()读取、position(long)设置,truncate(long size)截取Channel的数据,保留至指定大小长度。

ByteBuffer

UML

image-20210423165258518

Buffer是一个线性、有限的元素( 基本类型) 序列

Buffer中四个重要的变量:

  • capacity:容量。Buffer调用allocate是分配的内存大小,一旦创建不在改变
  • limit:第一个不能读或写的index,不能为负数或大于capacity
  • position:下一个写或读的index,不能为负数或者大于limit
  • mark:记录当前position的位置,mark初始化为-1,调用mark() 等于当前position,这样调用reset()时能将position的位置重置为mark的位置。如果没有调用mark()而调用reset()会报InvalidMarkException
  • 0<=mark<=position<=limit<=capacity

从上文FileChannel的方法也能看出,数据的载体是ByteBuffer。ByteBuffer定义了6中类型的操作:

  • 单字节的读写:get(),put (byte)

  • 字节数组的读写:get(byte[]),put(byte[])

  • 基本类型的读写:getDouble(int index),putDouble(double value)

  • 创建:allocate()或wrap(byte[])

  • 压缩(未读的数据移到缓冲区的起始处):compact()

  • 清空缓冲区:clear()

    public Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }
    
  • 翻转(limit设置为当前position,position为0):flip()

    public Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }
    
  • 重置:rewind(),这里没有重置limit,一般时读完一次数据后想再读一次时使用

    public Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }
    

DirectByteBuffer和HeapByteBuffer

示例1

@Test
public void testBuffer2 () throws IOException {
    // world!
    final FileInputStream inputStream = new FileInputStream("log/f1.txt");
    final File file = new File("log/f2.txt");
    file.createNewFile();

    final ByteBuffer buffer = ByteBuffer.allocate(1024);
    buffer.put("hello".getBytes());
    //继续将f1.txt填充至ByteBuffer中
    System.out.println(inputStream.getChannel().read(buffer));

    //须调用flip翻转
    buffer.flip();
    final FileOutputStream outputStream = new FileOutputStream(file);
    System.out.println(outputStream.getChannel().write(buffer));

    outputStream.close();
    inputStream.close();
}

简单展示了,手动put字符串到ByteBuffer中,已经将文件通过Channel读取到ByteBuffer中。

示例2

@Test
public void testBytebuffer3 () throws IOException {
    //获取当前系统文件的编码格式
    String encoding = System.getProperty("file.encoding");
    final Charset charset = Charset.forName(encoding);
    final CharsetDecoder decoder = charset.newDecoder();
    final File file = new File("log/1.txt");
    final FileInputStream fileInputStream = new FileInputStream(file);
    final FileChannel channel = fileInputStream.getChannel();
    final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    //将ByteBuffer同过charset转换为中文字符
    final CharBuffer charBuffer = CharBuffer.allocate(1024);
    byte[] temp;
    while (channel.read(byteBuffer)!=-1){
        byteBuffer.flip();//切换模式
        decoder.decode(byteBuffer, charBuffer, true);
        charBuffer.flip();
        System.out.print(charBuffer.toString());
        charBuffer.clear();
        //此时由于utf-8存储需要2个字节,所以再边界可能操作同一个字符被分割再两个ByteBuffer上
        //compact()可以将position和limit的字节移动到开头
        //否则会出现再边界处乱码,网上很多解决方法都是手动保存再一个byte[]数组再写入byteBuffer,
        //其实调用这个方法就可以了
        byteBuffer.compact();
    }
    fileInputStream.close();
}

如代码注释,解决读取文件时中文乱码

MappedByteBuffer

MappedByteBuffer通过FileChannel#map方法创建,将文件直接映射到虚拟内存,也称为内存映射,使用direct buffer

  • 通常inputStream的read方法需要将文件从硬盘拷贝到内核空间的缓冲区,再将数据拷贝到用户空间,实际进行了两次拷贝。
  • 而map方法通过内存映射技术,将文件直接映射到虚拟内存,这样可以直接将数据拷贝到用户空间。只进行一次拷贝。
  • 虚拟内存:虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。
  • 请参考;图解Linux中的虚拟内存机制和内存映射

MappedByteBuffer文件大小限制受系统的虚拟地址空间大小限制,如果cpu时32位则最大位4g。而java中map参数的size位long类型,但是在接口文档的参数解释上指明了大小不能超过java.lang.Integer#MAX_VALUE,换算过来大概2g

A mapped byte buffer and the file mapping that it represents remain valid until the buffer itself is garbage-collected 。MappedByteBuffer是没有close方法的,即使它的FileChannel被close了,MappedByteBuffer仍然处于打开状态,只有JVM进行垃圾回收的时候才会被关闭。而这个时间是不确定的。

MappedByteBuffer在数据量很小的时候,表现比较糟糕,那是因为direct buffer的初始化时间较长,所以建议大家只有在数据量较大的时候,在用MappedByteBuffer。

示例

@Test
public void testMappedByteBuffer () throws IOException {
    final File file = new File("log/fb.txt");
    file.createNewFile();
    final RandomAccessFile fileOutputStream = new RandomAccessFile(file,"rw");
    final FileChannel channel = fileOutputStream.getChannel();
    final MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, 1024);
    final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    byteBuffer.put(str.getBytes());
    byteBuffer.flip();
    //由于内存映射,这里只需要将字符串字节写入MappedByteBuffer
    map.put(byteBuffer);
    //RandomAccessFile关闭不意味着释放MappedByteBuffer占用的空间
    fileOutputStream.close();
}

资料

java.nio.charset

@Test
public void testCharSet() throws IOException {
    System.out.println(Charset.defaultCharset());
    final Charset charset = Charset.forName(StandardCharsets.UTF_8.name());
    System.out.println(charset);
    System.out.println(Charset.forName(StandardCharsets.ISO_8859_1.name()));
    final byte[] isoBytes = "我是谁?".getBytes(StandardCharsets.ISO_8859_1);
    System.out.println(isoBytes.length);
    final byte[] utfBytes = "我是谁?".getBytes(StandardCharsets.UTF_8);
    System.out.println(utfBytes.length);
    //???? 无法解析
    System.out.println(new String(isoBytes));
    //会自动创建该文件
    final File file = new File("log/fc.txt");
    final FileOutputStream fileOutputStream = new FileOutputStream(file);
    fileOutputStream.write("我是谁?我在哪里".getBytes());
    System.out.println("占用字节:"+"我=".getBytes().length);
    final FileInputStream fileInputStream = new FileInputStream(file);
    final byte[] bytes = new byte[4];
    fileInputStream.read(bytes);
    final CharBuffer charBuffer = CharBuffer.allocate(4);
    charset.newDecoder().decode(ByteBuffer.wrap(bytes), charBuffer, true);
    //万恶的flip方法
    charBuffer.flip();
    //String 默认使用utf-8解析字节数组,所以这里会多出一个字节导致乱码
    System.out.println(new String(bytes));
    System.out.println(charBuffer.toString());
    fileOutputStream.close();
}
  • StandardCharsets保存了常用的字符集常量,如StandardCharsets.UTF_8StandardCharsets.ISO_8859_1
  • new String(bytes)默认使用utf-8解析字节数组。
  • 按字节读取文件时,由于中文字符占用多个字节,在read(bytes)的过程中就有可以能将自己字符串的字节拆分到不同的字节数组来进行解析,此时可能导致乱码。需要使用Charset处理。
  • 参数通过URL传递的时候以http协议传递,格式为iso-8859-1。此时需要new String(request.getParameter(name).getBytes("ISO-8859-1"), "GBK"),一般http服务器可以配置相应的解析字符集

FileSystem

@Test
public void testFileSystem () throws IOException {
    final FileSystem fileSystem = FileSystems.getDefault();
    for (FileStore fileStore : fileSystem.getFileStores()) {
        System.out.println("名称:"+fileStore.name());
        System.out.println("    type:"+fileStore.type());
        System.out.println("    TotalSpace:"+fileStore.getTotalSpace()/1024d/1024d/1024d);
        System.out.println("    UsableSpace:"+fileStore.getUsableSpace()/1024d/1024d/1024d);
        System.out.println("    UnallocatedSpace:"+fileStore.getUnallocatedSpace()/1024d/1024d/1024d);
    }
}

返回系统各个分区的总大小、可用大小、已用大小

File 类的很多实现都依赖于FileSystem,比如length()delete() 等等基本所有方法依赖FileSystem的实现类

URI和URL

图片来源

URI 是统一资源标识符(uniform resource identifier URI),而 URL 是统一资源定位符(uniform resource locator URL)。笼统地说,每个 URL 都是 URI,但不一定每个 URI 都是 URL。

大白话,就是URI是抽象的定义,不管用什么方法表示,只要能定位一个资源,就叫URI,本来设想的的使用两种方法定位:1,URL,用地址定位;2,URN 用名称定位。

举个例子:去村子找个具体的人(URI),如果用地址:某村多少号房子第几间房的主人 就是URL, 如果用身份证号+名字 去找就是URN了。

结果就是 目前WEB上就URL流行开了,平常见得URI 基本都是URL。

在Java类库中,URI类不包含任何访问资源的方法,它唯一的作用就是解析。

相反的是,URL类可以打开一个到达资源的流。

因此URL类只能作用于那些 Java类库知道该如何处理的模式,

例如http:,https:,ftp:,本地文件系统(file:),和Jar文件(jar:)。

URL 编码

URL 只能使用 ASCII 字符集来通过因特网进行发送。

由于 URL 常常会包含 ASCII 集合之外的字符,URL 必须转换为有效的 ASCII 格式。

URL 编码使用 "%" 其后跟随两位的十六进制数来替换非 ASCII 字符。

URL 不能包含空格。URL 编码通常使用 + 来替换空格。

示例1

@Test
public void testPath () throws IOException {
    final Path path = Path.of("log","f2.txt");
    System.out.println(Files.exists(path));
    System.out.println(path.normalize());
    System.out.println(path.toAbsolutePath());
    final URI uri = path.toUri();
    final URL url = uri.toURL();
    //file:///D:/%5B4%5Dproject/springcloud/awesome-jdk-practice/log/f2.txt
    System.out.println(uri);
    //特殊符号或中文转码,如[会在uri中表示为%5B,所以需要URLDecoder解析
    System.out.println(URLDecoder.decode(uri.toString(),StandardCharsets.UTF_8));
    final InputStream inputStream = url.openStream();
    final byte[] bytes = new byte[1024];
    inputStream.read(bytes);
    System.out.println(new String(bytes));
    inputStream.close();
    //url获取http资源
    final URL baidu = new URL("https://www.baidu.com/");
    System.out.println(baidu);
    final URLConnection connection = baidu.openConnection();
    final InputStream baiduStream = connection.getInputStream();
    final byte[] baiduBytes = baiduStream.readAllBytes();
    System.out.println(new String(baiduBytes));
    baiduStream.close();
}

代码展示了文件系统类型的url作为InputStream处理

还展示了uri中特殊符号和中文需要使用URLDecoder解析

资料

Path

java.nio.file.Path是jdk7引入的新的接口,主要在Nio相关的操作中使用,大部分情况下与java.io.File相同

示例为Path的基本操作

@Test
public void testPathOp () throws IOException {
    //创建
    final Path basePath = Path.of("aaaa","bbb","ccc");
    //创建不存在的多级目录,aaaabbccc
    Files.createDirectories(basePath);
    final Path other = Path.of("test/log");
    //other为绝对路径直接返回,否则返回两个连接的路径:pathother
    //aaaabbccc	estlog
    System.out.println(basePath.resolve(other));
    //在basePath的上目录下连接other,如path..other
    //aaaabb	estlog
    System.out.println(basePath.resolveSibling(other));
    //返回在 basePath 下访问other的相对路径,一定要注意是在basePath下访问other的相对路径
    //......	estlog
    System.out.println(basePath.relativize(other));
    //这里很奇怪,输出结果还是......	estlog
    //此时的路径是相对于当前根目录,和上面相对与basePath不一样
    System.out.println(basePath.relativize(other).normalize());
    //这里保留了相对路径:D:[4]projectspringcloudawesome-jdk-practice......	estlog
    System.out.println(basePath.relativize(other).toAbsolutePath());
    //去掉了冗余的相对路径的符号D:	estlog
    System.out.println(basePath.relativize(other).toAbsolutePath().normalize());
    //比较
    final Path otherAbs = other.toAbsolutePath();
    //如果相等返回0,否则返回词典排序的差值
    System.out.println(other.compareTo(otherAbs));
    //这里是false
    System.out.println(other.equals(otherAbs));
}

PathMatcher

支持glob和regex两种语法。

glob:

  • *.java:匹配文件名以.java结束的Path
  • *.*:匹配文件名包含dot的Path
  • *.{java,class}:匹配以.java.class结尾的Path
  • foo.?:匹配文件名以foo.开头
  • /home/**:匹配home当前目录以及其子目录
  • /home/*:只匹配当前目录
  • *:在不跨文件目录边界上,匹配0个或多个字符
  • **:可以跨文件目录边界上,匹配0个或多个字符
  • ? :精确匹配一个字符
  • :转义字符串
  • [ ]:可匹配的字符集合,如[abc]可以匹配a或b或c,[a-z]匹配a到z的字符,[abce-g]匹配a,b,c,e,f,g,[!a-c]匹配 a,b,c之外的字符
  • { }:表示一个以逗号分隔pattern的集合

regex

参考java.util.regex.Pattern

示例

@Test
public void testPathMatcher() throws IOException {
    final Path parent = Path.of(".");
    final DirectoryStream<Path> paths = Files.newDirectoryStream(parent,"*.xml");
    paths.forEach(System.out::println);
    Files.newDirectoryStream(parent,path->path.toFile().isFile()).forEach(System.out::println);
    //底层也是使用newDirectoryStream
    Files.list(parent);
}

Files

目录遍历

@Test
public void testFileVisitor() throws IOException {
    //遍历目录
    final Path dpath = Path.of(".");
    //深度优先搜索返回指定Path下的所有文件和文件夹
    final Stream<Path> walk = Files.walk(dpath.toAbsolutePath());
    walk.forEach(System.out::println);
    System.out.println("=============");
    final Path source = Path.of("log");
    final Path target = Path.of("aaaa");
    //遍历目录树,并使用FileVisitor完成文件目录以及文件的拷贝
    Files.walkFileTree(source, new SimpleFileVisitor<>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            final Path targetdir = target.resolve(source.relativize(dir));
            Files.copy(dir, targetdir, StandardCopyOption.REPLACE_EXISTING);
            return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            Files.copy(file, target.resolve(source.relativize(file)), StandardCopyOption.REPLACE_EXISTING);
            return FileVisitResult.CONTINUE;
        }
    });
}
  • 使用walk遍历指定path下的文件和文件夹
  • walkFileTree遍历文件树的同时,分别对Directory和File执行相应的操作

生成文件MD5

@Test
public void testFileVisitor2 () throws IOException {
    final Path path = Path.of("D:\[4]project\springcloud\awesome-jdk-practice\src\main\java");
    Files.walkFileTree(path,new SimpleFileVisitor<>(){
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            System.out.println(file.getFileName()+"="+createFileMd5(file.toFile()));
            return FileVisitResult.CONTINUE;
        }
    });
}

与前文file.list的方式对比明显减少了代码量

文件读写

@Test
public void testFiles1 () throws IOException {
    //默认是UTF_8,这里也可以指定任意编码格式
    System.out.println(Files.readString(Path.of("log/fc.txt"), StandardCharsets.UTF_8));
    //默认写配置 Set.of(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING,StandardOpenOption.WRITE);
    System.out.println(Files.write(Path.of("log/ffs.txt"), "我知道你是谁!".getBytes(), StandardOpenOption.APPEND));
}

仅限于读取小的文件,因为这里会将文件内存全部导入内存

创建io对象

@Test
public void testFiles2() throws IOException {
   final Path path = Path.of("pom.xml");
   final BufferedReader reader = Files.newBufferedReader(path);
   System.out.println(reader.readLine());
   reader.close();
   final Path file = Path.of("log/fw.txt");
   final BufferedWriter writer = Files.newBufferedWriter(file);
   writer.write(str);
   writer.close();
   System.out.println(Files.size(file));
   System.out.println(file.toFile().length());
}
原文地址:https://www.cnblogs.com/froggengo/p/14703757.html