• Netty(2)文件编程(前置了解知识)


    文件编程

    即对文件进行操作

    FileChannel

    FileChannel 只能工作在阻塞模式下

    获取getChannel

    不能直接打开 FileChannel,只能通过下面的方法进行获取:

    1. 通过 FileInputStream 获取的 channel 只能读
    2. 通过 FileOutputStream 获取的 channel 只能写
    3. 通过 RandomAccessFile 是否能读写根据构造 RandomAccessFile 时的读写模式决定,并指定其读写模式为rw
      //可读可写
      RandomAccessFile file = new RandomAccessFile("test.txt", "rw").getChannel();
      //可读
      RandomAccessFile file = new RandomAccessFile("test.txt", "r").getChannel();
      //可写
      RandomAccessFile file = new RandomAccessFile("test.txt", "w").getChannel();
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

    读取read

    从 channel 读取数据填充 ByteBuffer,返回值表示读到了多少字节,-1 表示到达了文件的末尾

    // readBytes=-1 的话表示到达了文件的末尾
    int readBytes = channel.read(buffer);
    
    • 1
    • 2

    写入write

    以后使用的多是SocketChannel,SocketChannel传输数据的能力是有限的,不能因为buffer有多少数据,就一次性将这些数据全部写入到SocketChannel中,正确的写入步骤如下:

    //正确的写入步骤
    //在 while 中调用 channel.write 是因为 write 方法并不能保证一次将 buffer 中的内容全部写入 chann
    while(buffer.hasRemaining()) {
        channel.write(buffer);
    }
    //fileChannel二者都可以
    ByteBuffer buffer = ...;
    buffer.put(...); // 存入数据
    buffer.flip();   // 切换读模式
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    关闭close

    channel 必须关闭
    关闭有两种关闭方式:

    1. 调用FileInputStream、FileOutputStream 或者 RandomAccessFile 的 close 方法(会间接调用 channel 的 close 方法)
    2. 调用 channel 的 close 方法

    以后直接使用channel.close()去关闭即可

    位置position

    获取当前位置

    long pos = channel.position();
    
    • 1

    设置当前位置

    long newPos = ...;
    channel.position(newPos);
    
    • 1
    • 2

    设置当前位置时,如果设置为文件的末尾

    • 这时读取会返回 -1
    • 这时写入,会追加内容,但要注意如果 position 超过了文件末尾,再写入时在新内容和原末尾之间会有空洞(00)

    大小size

    使用 size 方法获取文件的大小

    强制写入force

    操作系统出于性能的考虑,会将数据缓存,不是立刻写入磁盘,当channel关闭的时候才会把所有的缓存数据同步到磁盘中。

    可以调用 force(true) 方法将文件内容和元数据(文件的权限等信息)立刻写入磁盘

    channel传输工具

    transferTo(不超过2g)

    从一个channel将数据传输到另一个channel

    public static void main(String[] args) {
     try (
               //数据从哪来
               FileChannel from = new FileInputStream("data.txt").getChannel();
               //往哪写
               FileChannel to = new FileOutputStream("to.txt").getChannel();
       ) {
           // 效率高,底层会利用操作系统的零拷贝进行优化, 一次只能传2g 数据,多的传输不过去
           //从0开始传
           from.transferTo(0, left, to);
       } catch (IOException e) {
           e.printStackTrace();
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    超过2g

    public static void main(String[] args) {
        try (
                //数据从哪来
                FileChannel from = new FileInputStream("data.txt").getChannel();
                //往哪写
                FileChannel to = new FileOutputStream("to.txt").getChannel();
        ) {
        	//文件大小
            long size = from.size();
            // left 变量代表还剩余多少字节
            for (long left = size; left > 0; ) {
                System.out.println("position:" + (size - left) + " left:" + left);
                //transferTo返回的结果是实际传输的字节数
                left -= from.transferTo((size - left), left, to);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Path

    jdk7 引入了 Path 和 Paths 类

    • Path 用来表示文件路径

    • Paths 是工具类,用来获取 Path 实例

      Path source = Paths.get("1.txt"); // 相对路径 使用 user.dir 环境变量来定位 1.txt
      
      //这种 \ 需要转义,所有是\\
      Path source = Paths.get("d:\\1.txt"); // 绝对路径 代表了  d:\1.txt
      
      Path source = Paths.get("d:/1.txt"); // 绝对路径 同样代表了  d:\1.txt
      
      Path projects = Paths.get("d:\\data", "projects"); // 代表了  d:\data\projects
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    . 代表了当前路径
    .. 代表了上一级路径
    测试:

    Path path = Paths.get("d:\\data\\projects\\a\\..\\b");
    System.out.println(path);//
    System.out.println(path.normalize()); // 正常化路径
    
    /*
    目录结构:
    	d:
    		|- data
    			|- projects
    				|- a
    				|- b
    
    normalize():将路径正常化
    会输出如下内容:
    	d:\data\projects\a\..\b
    	d:\data\projects\b
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Files

    检查文件是否存在exists

    存在返回true

    Path path = Paths.get("helloword/data.txt");
    System.out.println(Files.exists(path));
    
    
    • 1
    • 2
    • 3

    创建一级目录createDirectory

    如果目录已存在,会抛异常 FileAlreadyExistsException

    不能一次创建多级目录,否则会抛异常 NoSuchFileException

    只能创建一级目录

    Path path = Paths.get("helloword/d1");
    Files.createDirectory(path);
    
    
    • 1
    • 2
    • 3

    创建多级目录createDirectories

    创建多级目录,即使中间那一级目录不存在也会创建

    Path path = Paths.get("helloword/d1/d2");
    Files.createDirectories(path);
    
    
    • 1
    • 2
    • 3

    拷贝文件copy

    如果文件已存在,会抛异常 FileAlreadyExistsException

    Path source = Paths.get("helloword/data.txt");
    Path target = Paths.get("helloword/target.txt");
    //从source复制到target,效率比较高,操作系统做的实现
    Files.copy(source, target);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果希望用 source 覆盖掉 target,需要用 StandardCopyOption 来控制(不管目标存不存在,都会替换掉target)

    Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
    
    
    • 1
    • 2

    以后拷贝文件,要么使用transferTo,要么使用copy,二者效率差不多

    移动文件move

    Path source = Paths.get("helloword/data.txt");
    Path target = Paths.get("helloword/data.txt");
    
    Files.move(source, target);
    //StandardCopyOption.ATOMIC_MOVE 保证文件移动的原子性
    Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    删除文件delete

    如果文件不存在,会抛异常 NoSuchFileException

    Path target = Paths.get("helloword/target.txt");
    
    Files.delete(target);
    
    
    • 1
    • 2
    • 3
    • 4

    删除目录delete

    如果目录还有内容(这个目录里面还有文件或文件夹),会抛异常 DirectoryNotEmptyException
    也就是说,只能删除空的目录

    Path target = Paths.get("helloword/d1");
    
    Files.delete(target);
    
    
    • 1
    • 2
    • 3
    • 4

    遍历目录文件walkFileTree

    private static void m1() throws IOException {
    	AtomicInteger dirCount = new AtomicInteger();
    	AtomicInteger fileCount = new AtomicInteger();
    	
    	//walkFileTree:该方法有几种重载方法,基本上都可以通过类名判断出是什么用途
    	/*
    		preVisitDirectory:进入文件前
    		visitFile:进入文件后
    		postVistiDirectory:退出文件前
    	*/
    	Files.walkFileTree(Paths.get("C:\\xxx"), new SimpleFileVisitor<Path>(){
    	    @Override
    	    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
    	        //这个遍历文件夹时包括当前的文件夹
    	        System.out.println("====>"+dir);
    	        dirCount.incrementAndGet();
    	        //return的内容不能修改,需要添加的逻辑在其上面执行即可
    	        return super.preVisitDirectory(dir, attrs);
    	    }
    	
    	    @Override
    	    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
    	        System.out.println(file);
    	        fileCount.incrementAndGet();
    	        return super.visitFile(file, attrs);
    	    }
    	});
    	System.out.println("dir count:" +dirCount);
    	System.out.println("file count:" +fileCount);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    统计 jar 的数目

    Path path = Paths.get("C:\\xxx");
    AtomicInteger fileCount = new AtomicInteger();
    Files.walkFileTree(path, new SimpleFileVisitor<Path>(){
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
            throws IOException {
            if (file.toFile().getName().endsWith(".jar")) {
                fileCount.incrementAndGet();
            }
            return super.visitFile(file, attrs);
        }
    });
    System.out.println(fileCount); // 724
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    删除多级目录walkFileTree

    删除是危险操作,确保要递归删除的文件夹没有重要内容

    Path path = Paths.get("d:\\xxx");
    Files.walkFileTree(path, new SimpleFileVisitor<Path>(){
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
            throws IOException {
            Files.delete(file);
            return super.visitFile(file, attrs);
        }
    
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) 
            throws IOException {
            Files.delete(dir);
            return super.postVisitDirectory(dir, exc);
        }
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    拷贝多级目录walk

    long start = System.currentTimeMillis();
    String source = "D:\\Snipaste-1.16.2-x64";
    String target = "D:\\Snipaste-1.16.2-x64aaa";
    
    Files.walk(Paths.get(source)).forEach(path -> {
        try {
        	//将名称进行替换
            String targetName = path.toString().replace(source, target);
            // 是目录
            if (Files.isDirectory(path)) {
            	//创建目录
                Files.createDirectory(Paths.get(targetName));
            }
            // 是普通文件
            else if (Files.isRegularFile(path)) {
            	//拷贝文件
                Files.copy(path, Paths.get(targetName));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    });
    long end = System.currentTimeMillis();
    System.out.println(end - start);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    git常用的几个命令
    POST 请求,Ajax 与 cookie
    ConvMAE(2022-05)
    AI Studio vLoong能源AI挑战赛——异常检测赛A榜第三名方案
    新媒体营销实训解决方案
    DOM
    循环神经网络 - 序列模型
    学生用什么台灯最好?分享专业的学生护眼台灯
    服务器租用多少钱一年?
    IINA for Mac v1.3.5 音视频软件 安装教程(保姆级)
  • 原文地址:https://blog.csdn.net/yyuggjggg/article/details/126168707