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();
    

读取read

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

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

写入write

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

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

关闭close

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

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

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

位置position

获取当前位置

long pos = channel.position();

设置当前位置

long newPos = ...;
channel.position(newPos);

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

  • 这时读取会返回 -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();
   }
}

超过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();
    }
}

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
    

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

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
*/

Files

检查文件是否存在exists

存在返回true

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

创建一级目录createDirectory

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

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

只能创建一级目录

Path path = Paths.get("helloword/d1");
Files.createDirectory(path);

创建多级目录createDirectories

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

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

拷贝文件copy

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

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

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

Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);

以后拷贝文件,要么使用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);

删除文件delete

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

Path target = Paths.get("helloword/target.txt");

Files.delete(target);

删除目录delete

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

Path target = Paths.get("helloword/d1");

Files.delete(target);

遍历目录文件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);
}

统计 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

删除多级目录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);
    }
});

拷贝多级目录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);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

?abc!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值