Java NIO Files 操作文件

文章来源:http://blog.sina.com.cn/s/blog_9f8ffdaf0102wpeo.html

Java7中文件IO发生了很大的变化,专门引入了很多新的类:

import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;

......等等,来取代原来的基于java.io.File的文件IO操作方式.

1.Path就是取代File的

Path represents apath that is hierarchical and composed of a sequence of directoryand file name elements separated by a special separator ordelimiter.

Path用于来表示文件路径和文件。可以有多种方法来构造一个Path对象来表示一个文件路径,或者一个文件:

1)首先是final类Paths的两个static方法,如何从一个路径字符串来构造Path对象:

        Path path = Paths.get("C:/", "Xmp");
        Path path2 = Paths.get("C:/Xmp");
        
        URI u = URI.create("file:///C:/Xmp/dd");        
        Path p = Paths.get(u);

2)FileSystems构造:

Path path3 = FileSystems.getDefault().getPath("C:/", "access.log");

3)File和Path之间的转换,File和URI之间的转换:

        File file = new File("C:/my.ini");
        Path p1 = file.toPath();
        p1.toFile();
        file.toURI();

4)创建一个文件:

复制代码
        Path target2 = Paths.get("C:\\mystuff.txt");
//      Set perms = PosixFilePermissions.fromString("rw-rw-rw-");
//      FileAttribute> attrs = PosixFilePermissions.asFileAttribute(perms);
        try {
            if(!Files.exists(target2))
                Files.createFile(target2);
        } catch (IOException e) {
            e.printStackTrace();
        }
复制代码

windows下不支持PosixFilePermission来指定rwx权限。

5)Files.newBufferedReader读取文件:

复制代码
        try {
//            Charset.forName("GBK")
            BufferedReader reader = Files.newBufferedReader(Paths.get("C:\\my.ini"), StandardCharsets.UTF_8);
            String str = null;
            while((str = reader.readLine()) != null){
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
复制代码

可以看到使用 Files.newBufferedReader远比原来的FileInputStream,然后BufferedReader包装,等操作简单的多了。

这里如果指定的字符编码不对,可能会抛出异常 MalformedInputException ,或者读取到了乱码:

复制代码
java.nio.charset.MalformedInputException: Input length = 1
    at java.nio.charset.CoderResult.throwException(CoderResult.java:281)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:339)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.BufferedReader.fill(BufferedReader.java:161)
    at java.io.BufferedReader.readLine(BufferedReader.java:324)
    at java.io.BufferedReader.readLine(BufferedReader.java:389)
    at com.coin.Test.main(Test.java:79)
复制代码

6)文件写操作:

复制代码
        try {
            BufferedWriter writer = Files.newBufferedWriter(Paths.get("C:\\my2.ini"), StandardCharsets.UTF_8);
            writer.write("测试文件写操作");
            writer.flush();
            writer.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
复制代码

7)遍历一个文件夹

复制代码
        Path dir = Paths.get("D:\\webworkspace");
        try(DirectoryStream stream = Files.newDirectoryStream(dir)){
            for(Path e : stream){
                System.out.println(e.getFileName());
            }
        }catch(IOException e){
            
        }
复制代码
复制代码
        try (Stream stream = Files.list(Paths.get("C:/"))){
            Iterator ite = stream.iterator();
            while(ite.hasNext()){
                Path pp = ite.next();
                System.out.println(pp.getFileName());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
复制代码

上面是遍历单个目录,它不会遍历整个目录。遍历整个目录需要使用:Files.walkFileTree

8)遍历整个文件目录:

复制代码
    public static void main(String[] args) throws IOException{
        Path startingDir = Paths.get("C:\\apache-tomcat-8.0.21");
        List result = new LinkedList();
        Files.walkFileTree(startingDir, new FindJavaVisitor(result));
        System.out.println("result.size()=" + result.size());        
    }
    
    private static class FindJavaVisitor extends SimpleFileVisitor{
        private List result;
        public FindJavaVisitor(List result){
            this.result = result;
        }
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs){
            if(file.toString().endsWith(".java")){
                result.add(file.getFileName());
            }
            return FileVisitResult.CONTINUE;
        }
    }
复制代码

来一个实际例子:

复制代码
    public static void main(String[] args) throws IOException {
        Path startingDir = Paths.get("F:\\upload\\images");    // F:\\upload\\images\\2\\20141206
        List result = new LinkedList();
        Files.walkFileTree(startingDir, new FindJavaVisitor(result));
        System.out.println("result.size()=" + result.size()); 
        
        System.out.println("done.");
    }
    
    private static class FindJavaVisitor extends SimpleFileVisitor{
        private List result;
        public FindJavaVisitor(List result){
            this.result = result;
        }
        
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs){
            String filePath = file.toFile().getAbsolutePath();       
            if(filePath.matches(".*_[1|2]{1}\\.(?i)(jpg|jpeg|gif|bmp|png)")){
                try {
                    Files.deleteIfExists(file);
                } catch (IOException e) {
                    e.printStackTrace();
                }
              result.add(file.getFileName());
            } return FileVisitResult.CONTINUE;
        }
    }
复制代码

将目录下面所有符合条件的图片删除掉:filePath.matches(".*_[1|2]{1}\\.(?i)(jpg|jpeg|gif|bmp|png)")

 

复制代码
    public static void main(String[] args) throws IOException {
        Path startingDir = Paths.get("F:\\111111\\upload\\images");    // F:\111111\\upload\\images\\2\\20141206
        List result = new LinkedList();
        Files.walkFileTree(startingDir, new FindJavaVisitor(result));
        System.out.println("result.size()=" + result.size()); 
        
        System.out.println("done.");
    }
    
    private static class FindJavaVisitor extends SimpleFileVisitor{
        private List result;
        public FindJavaVisitor(List result){
            this.result = result;
        }
        
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs){
            String filePath = file.toFile().getAbsolutePath();
            int width = 224;
            int height = 300;
            StringUtils.substringBeforeLast(filePath, ".");
            String newPath = StringUtils.substringBeforeLast(filePath, ".") + "_1." 
                                            + StringUtils.substringAfterLast(filePath, ".");
            try {
                ImageUtil.zoomImage(filePath, newPath, width, height);
            } catch (IOException e) {
                e.printStackTrace();
                return FileVisitResult.CONTINUE;
            }
            result.add(file.getFileName());
            return FileVisitResult.CONTINUE;
        }
    }
复制代码

 

为目录下的所有图片生成指定大小的缩略图。a.jpg 则生成 a_1.jpg

 

2.强大的java.nio.file.Files

1)创建目录和文件:

复制代码
        try {
            Files.createDirectories(Paths.get("C://TEST"));
            if(!Files.exists(Paths.get("C://TEST")))
                    Files.createFile(Paths.get("C://TEST/test.txt"));
//            Files.createDirectories(Paths.get("C://TEST/test2.txt"));
        } catch (IOException e) {
            e.printStackTrace();
        }
复制代码

注意创建目录和文件Files.createDirectories 和Files.createFile不能混用,必须先有目录,才能在目录中创建文件。

2)文件复制:

从文件复制到文件:Files.copy(Path source, Path target, CopyOptionoptions);

从输入流复制到文件:Files.copy(InputStream in, Path target, CopyOptionoptions);

从文件复制到输出流:Files.copy(Path source, OutputStream out);

复制代码
        try {
            Files.createDirectories(Paths.get("C://TEST"));
            if(!Files.exists(Paths.get("C://TEST")))
                    Files.createFile(Paths.get("C://TEST/test.txt"));
//          Files.createDirectories(Paths.get("C://TEST/test2.txt"));
            Files.copy(Paths.get("C://my.ini"), System.out);
            Files.copy(Paths.get("C://my.ini"), Paths.get("C://my2.ini"), StandardCopyOption.REPLACE_EXISTING);
            Files.copy(System.in, Paths.get("C://my3.ini"), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }
复制代码

3)遍历一个目录和文件夹上面已经介绍了:Files.newDirectoryStream, Files.walkFileTree

4)读取文件属性:

            Path zip = Paths.get(uri);
            System.out.println(Files.getLastModifiedTime(zip));
            System.out.println(Files.size(zip));
            System.out.println(Files.isSymbolicLink(zip));
            System.out.println(Files.isDirectory(zip));
            System.out.println(Files.readAttributes(zip, "*"));

5)读取和设置文件权限:

复制代码
            Path profile = Paths.get("/home/digdeep/.profile");
            PosixFileAttributes attrs = Files.readAttributes(profile, PosixFileAttributes.class);// 读取文件的权限
            Set posixPermissions = attrs.permissions();
            posixPermissions.clear();
            String owner = attrs.owner().getName();
            String perms = PosixFilePermissions.toString(posixPermissions);
            System.out.format("%s %s%n", owner, perms);
            
            posixPermissions.add(PosixFilePermission.OWNER_READ);
            posixPermissions.add(PosixFilePermission.GROUP_READ);
            posixPermissions.add(PosixFilePermission.OTHERS_READ);
            posixPermissions.add(PosixFilePermission.OWNER_WRITE);
            
            Files.setPosixFilePermissions(profile, posixPermissions);    // 设置文件的权限
复制代码

Files类简直强大的一塌糊涂,几乎所有文件和目录的相关属性,操作都有想要的api来支持。这里懒得再继续介绍了,详细参见 jdk8的文档。

 

一个实际例子:

复制代码
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class StringTools {
    public static void main(String[] args) {
        try {
            BufferedReader reader = Files.newBufferedReader(Paths.get("C:\\Members.sql"), StandardCharsets.UTF_8);
            BufferedWriter writer = Files.newBufferedWriter(Paths.get("C:\\Members3.txt"), StandardCharsets.UTF_8);

            String str = null;
            while ((str = reader.readLine()) != null) {
                if (str != null && str.indexOf(", CAST(0x") != -1 && str.indexOf("AS DateTime)") != -1) {
                    String newStr = str.substring(0, str.indexOf(", CAST(0x")) + ")";
                    writer.write(newStr);
                    writer.newLine();
                }
            }
            writer.flush();
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

移动,复制和删除文件

移动文件树是复制和删除的文件树的结合。实际上,有两种方式来完成文件的移动。

一种是使用Files.move(),Files.copy(), 和Files.delete() 这三个方法;

另一种是只使用Files.copy(),Files.delete()方法。基于你的选择,在实现FileVisitor接口中对应的方法完成文件树的移动。

下面是一些注意事项。

  • 在移动任何目录或文件之前,首先要移动目录本身。因为不为空的目录不能被移动。你需要在PReVisitDirectory()方法中你使用 Files.copy()方法用拷贝文件来代替。
  • visitFile()是用来移动每个文件最合适的方式。你一可以使用Files.move()或联合使用Files.copy()Files.delete()方法。
  • 当你移动完所有的源目录到目标目录后,使用Files.delete()来删除源目录,此时应该已经为空。这些操作应该在postVisitDirectory()方法中完成。
  • 当你拷贝目录或文件时,你需要决定使用REPLACE_EXISTING 和 COPY_ATTRIBUTES选项。同时,当你移动文件或目录时,决定是否要使用ATOMIC_MOVE选项。
  • 如果你想保留源目录的属性,你需要在文件移动以后在postVisitDirectory()做这些操作。一些属性例如 lastModifiedTime 应该在preVisitDirectory()方法中提取直到在postVisitDirectory()方法中设置好后保存。这是因为你在从源目录移动完文件以后,文件内容已经更改,最初的最近修改时间已经被新的日期覆盖了。
  • 如果文件不能访问,你自己来决定来使用FileVisitResult.CONTINUE 或 TERMINATE 选项。
  • 你也可以使用FOLLOW_LINKS选项来跟踪符号链接文件,需要注意的是,只会移动符号链接本身文件,不会影响到它指向的实际文件。
  • 下面的代码把 C:\rafaelnadal目录移动到C:\ATP\players\rafaelnafal目录。
  •  importjava.io.IOException;
  •  import java.nio.file.FileVisitOption;
  •  import java.nio.file.FileVisitResult;
  •  import java.nio.file.FileVisitor;
  •  import java.nio.file.Files;
  •  import java.nio.file.Path;
  •  import java.nio.file.Paths;
  •  importjava.nio.file.attribute.BasicFileAttributes;
  •  import java.nio.file.attribute.FileTime;
  •  import java.util.EnumSet;
  •  import staticjava.nio.file.StandardCopyOption.REPLACE_EXISTING;
  •  import staticjava.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
  •  import staticjava.nio.file.StandardCopyOption.ATOMIC_MOVE;
  •  
  • classMoveTree implements FileVisitor {
  •  
  •    private final PathmoveFrom;
  •     private final PathmoveTo;
  •     static FileTime time =null;
  •  
  •    public MoveTree(Path moveFrom,Path moveTo) {
  •         this.moveFrom = moveFrom;
  •         this.moveTo = moveTo;
  •     }
  •  
  •    static void moveSubTree(PathmoveFrom, Path moveTo) throws IOException {
  •         try {
  •            Files.move(moveFrom, moveTo, REPLACE_EXISTING,ATOMIC_MOVE);
  •         } catch (IOException e) {
  •            System.err.println("Unable to move " + moveFrom +" [" + e + "]");
  •         }
  •  
  •    }
  •  
  •    @Override
  •     public FileVisitResultpostVisitDirectory(Object dir, IOExceptionexc) 
  •                                                           throws IOException {
  •         Path newdir =moveTo.resolve(moveFrom.relativize((Path) dir));
  •         try {
  •            Files.setLastModifiedTime(newdir, time);
  •            Files.delete((Path) dir);
  •         } catch (IOException e) {
  •            System.err.println("Unable to copy all attributesto: " + newdir+" [" + e + "]");
  •         }
  •  
  •        return FileVisitResult.CONTINUE;
  •     }
  •  
  •    @Override
  •     public FileVisitResultpreVisitDirectory(Object dir, BasicFileAttributesattrs) 
  •                                                              throwsIOException {
  •         System.out.println("Movedirectory: " + (Path) dir);
  •         Path newdir =moveTo.resolve(moveFrom.relativize((Path) dir));
  •         try {
  •            Files.copy((Path) dir, newdir, REPLACE_EXISTING,COPY_ATTRIBUTES);
  •            time = Files.getLastModifiedTime((Path)dir);
  •         } catch (IOException e) {
  •            System.err.println("Unable to move " + newdir + "[" + e + "]");
  •            return FileVisitResult.SKip_SUBTREE;
  •         }
  •  
  •        return FileVisitResult.CONTINUE;
  •     }
  •  
  •    @Override
  •     public FileVisitResultvisitFile(Object file, BasicFileAttributesattrs) 
  •                                                              throwsIOException {
  •         System.out.println("Move file:" + (Path) file);
  •         moveSubTree((Path) file,moveTo.resolve(moveFrom.relativize((Path) file)));
  •         returnFileVisitResult.CONTINUE;
  •     }
  •  
  •    @Override
  •     public FileVisitResultvisitFileFailed(Object file, IOExceptionexc) 
  •                                                              throwsIOException {
  •         returnFileVisitResult.CONTINUE;
  •     }
  •  }
  •  
  • class Main{
  •  
  •     public static voidmain(String[] args) throws IOException {
  •  
  •        Path moveFrom =Paths.get("C:/rafaelnadal");
  •         Path moveTo =Paths.get("C:/ATP/players/rafaelnadal");
  •  
  •        MoveTree walk = new MoveTree(moveFrom,moveTo);
  •         EnumSet opts =EnumSet.of(FileVisitOption.FOLLOW_LINKS);
  •  
  •        Files.walkFileTree(moveFrom, opts,Integer.MAX_VALUE, walk);
  •      }
  •  }
  • 你也可以使用其他的方法来完成Files.move()相同的工作,因为每一次移动就是一对拷贝删除操作。所以,你可以重写moveSubTree()方法。

  • staticvoid moveSubTree(Path moveFrom, Path moveTo) throws IOException{
  •         try {
  •            Files.copy(moveFrom, moveTo, REPLACE_EXISTING,COPY_ATTRIBUTES);
  •            Files.delete(moveFrom);
  •         } catch (IOException e) {
  •            System.err.println("Unable to move " + moveFrom +" [" + e + "]");
  •         }
  •     } 

你可能感兴趣的:(java)