第九节 netty前传-NIO 补充Path和File

NIO Path

Java NIO.Path接口位于java.nio.file包中,所以Java Path接口的完全限定名称是java.nio.file.Path。

Java Path实例表示文件系统中的路径。路径可以是绝对的或相对的。绝对路径包含从文件系统根目录到其指向的文件或目录的完整路径。相对路径包含相对于其他路径的文件或目录的路径。
很多时候,java.nio.file.Path接口类似于java.io.File类,但存在一些细微差别。在许多情况下,甚至可以使用Path接口替换File类的使用。

  1. 创建path实例

Path path = Paths.get("c:\data\myfile.txt");
上面的Path 为接口,Paths为工厂类

当然上面为决定路径,也可以创建相对路径

//相对路径,第一个参数作为相对的参考
Path path = Paths.get("d:\\base",
                       "..\\revlet-path");

NIO file

java.nio.file.Files的Files类提供了几种操作文件的方法

  1. 检查文件是否存在
//文件路径
Path path = Paths.get("data/logging.properties");
//注意第二个参数,的含义作为判断文件是否存在的判断依据,此处表示不依赖文件连接
boolean pathExists =
        Files.exists(path,
            new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});
  1. 创建目录
Path path = Paths.get("data/subdir");
try {
    Path newDir = Files.createDirectory(path);
} catch(FileAlreadyExistsException e){
    // the directory already exists.
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}
  1. 拷贝
Path sourcePath      = Paths.get("data/logging.properties");
Path destinationPath = Paths.get("data/logging-copy.properties");
try {
    Files.copy(sourcePath, destinationPath);
} catch(FileAlreadyExistsException e) {
    //destination file already exists
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}
  1. 移动文件
Path sourcePath      = Paths.get("data/logging-copy.properties");
Path destinationPath = Paths.get("data/subdir/logging-moved.properties");

try {
    Files.move(sourcePath, destinationPath,
            StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
    //moving file failed.
    e.printStackTrace();
}
  1. 删除文件
Path path = Paths.get("data/subdir/logging-moved.properties");
try {
    Files.delete(path);
} catch (IOException e) {
    //deleting file failed
    e.printStackTrace();
}
  1. 递归遍历目录树的功能。

Files.walkFileTree()方法用于递归遍历目录树的功能。参数包含Path实例和FileVisitor作为参数。FileVisitor是一个接口,业务功能需要自己实现

Files.CONTINUE表示文件遍历应该正常继续。

TERMINATE表示文件遍历应立即终止。

SKIP_SIBLINGS意味着文件遍历应该继续但不访问此文件或目录的任何兄弟。

SKIP_SUBTREE表示文件遍历应该继续但不访问此目录中的条目。(path, new FileVisitor<Path>() {
//在访问任何目录之前调用
  @Override
  public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
    System.out.println("pre visit dir:" + dir);
    return FileVisitResult.CONTINUE;
  }
//在文件遍历期间访问的每个文件(不是目录)都会调用visitFile()方法
  @Override
  public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
    System.out.println("visit file: " + file);
//返回值
//CONTINUE表示文件遍历应该正常继续。
//TERMINATE表示文件遍历应立即终止。
//SKIP_SIBLINGS意味着文件遍历应该继续但不访问此文件或目录的任何并列文件。
//SKIP_SUBTREE表示文件遍历应该继续但不访问此目录中的条目。
    return FileVisitResult.CONTINUE;
  }
//访问文件失败,则调用visitFileFailed()方法
  @Override
  public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
    System.out.println("visit file failed: " + file);
    return FileVisitResult.CONTINUE;
  }
// 访问目录后立即调用postVisitDirectory()方法。
  @Override
  public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
    System.out.println("post visit directory: " + dir);
    return FileVisitResult.CONTINUE;
  }
});
  • walkFileTree 的使用例子
/**
*Files.walkFileTree()递归查询文件
*/
Path rootPath = Paths.get("data");
String fileToFind = File.separator + "README.txt";
//SimpleFileVisitor继承接口FileVisitor,不过已经帮我我们实现了部分功能
try {
  Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
      String fileString = file.toAbsolutePath().toString();
      //System.out.println("pathString = " + fileString);

      if(fileString.endsWith(fileToFind)){
        System.out.println("file found at path: " + file.toAbsolutePath());
        return FileVisitResult.TERMINATE;
      }
      return FileVisitResult.CONTINUE;
    }
  });
} catch(IOException e){
    e.printStackTrace();
}

/**
*Files.walkFileTree()也可用于删除包含其中所有文件和子目录的目录。
*Files.delete()方法只会删除目录为空的目录。 通过浏览所有目录并删除每个目录
*中的所有文件(在visitFile()内部,然后删除目录本身(在postVisitDirectory()
*内),您可以删除包含所有子目录和文件的目录。 这是一个递归目录删除示例
*/
Path rootPath = Paths.get("data/to-delete");
try {
  Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
      System.out.println("delete file: " + file.toString());
      Files.delete(file);
      return FileVisitResult.CONTINUE;
    }
    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
      Files.delete(dir);
      System.out.println("delete dir: " + dir.toString());
      return FileVisitResult.CONTINUE;
    }
  });
} catch(IOException e){
  e.printStackTrace();
}

异步文件模式

在前面我们已经了解到,正常nio的文件IO也是阻塞模式,而这里的异步IO可以说是对非阻塞IO的一种补充。从Java 7后加入AsynchronousFileChannel于NIO中。 AsynchronousFileChannel可以异步从文件中读取写入数据。

所谓的异步文件读写,其实是使用java中Future的方式返回,所以调用读写方法时,会立刻返回future对象(可以对比java中的Future在多线程下的使用案例),当然后续从future读取会形成阻塞

简单使用介绍

  1. 从文件异步读取
//获取路径
Path path = Paths.get("home/test.txt");
//第二个参数表示,操作类型
AsynchronousFileChannel fileChannel =
    AsynchronousFileChannel.open(path, StandardOpenOption.READ);
//和前面一样,从channel中读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> operation = fileChannel.read(buffer, 0);
  • 注意与普通阻塞不同的是,执行完,buffer中是否有数据我们不知道,所以需要如下的判断凡是
Future<Integer> operation = fileChannel.read(buffer, position);
//这里为阻塞等待
while(!operation.isDone());
  buffer.flip();
 byte[] data = new byte[buffer.limit()];
 buffer.get(data);
System.out.println(new String(data));
buffer.clear();

当然上面的判断也可以使用前面介绍的CompletionHandler作为参数实现

fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
    @Override
    public void completed(Integer result, ByteBuffer attachment) {
        System.out.println("result = " + result);

        attachment.flip();
        byte[] data = new byte[attachment.limit()];
        attachment.get(data);
        System.out.println(new String(data));
        attachment.clear();
    }

    @Override
    public void failed(Throwable exc, ByteBuffer attachment) {

    }
});
  1. 写入操作
  • 和读取操作相同写入操作也的两种实现方式,也都是调用write方法,唯一不同的是参数
    第一种直接写入future
Path path = Paths.get("data/test-write.txt");
AsynchronousFileChannel fileChannel = 
    AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
buffer.put("test data".getBytes());
buffer.flip();
//写入数据,返回future对象
Future<Integer> operation = fileChannel.write(buffer, position);
buffer.clear();
//通过判断future是否写入完成(此判断是阻塞进行的)
while(!operation.isDone());
System.out.println("Write done");

第二种,和读取相同使用CompletionHandler作为参数的方式实现

Path path = Paths.get("data/test-write.txt");
if(!Files.exists(path)){
    Files.createFile(path);
}
AsynchronousFileChannel fileChannel = 
    AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
buffer.put("test data".getBytes());
buffer.flip();
//写入操作,这里再写入完成后会,自动回调实现的completed方法,
//这里可以根据自身业务做相关调整
fileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
    @Override
    public void completed(Integer result, ByteBuffer attachment) {
        System.out.println("bytes written: " + result);
    }
    @Override
    public void failed(Throwable exc, ByteBuffer attachment) {
        System.out.println("Write failed");
        exc.printStackTrace();
    }
});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容