This is the 11th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021

FileChannel

A FileChannel is a channel that operates on a file. Note that a FileChannel only works in blocking mode and therefore cannot be matched with a Selector.

Create FileChannel

There is no constructor for FileChannel, but a FileChannel is retrieved by FileInputStream, FileOutputStream, or RandomAccessFile, which all have a getChannel method, But their use is different:

  • FileChannel obtained by FileInputStream can only be read
  • FileChannel obtained via FileOutputStream can only be written
  • Whether a FileChannel retrieved from RandomAccessFile can read and write depends on the read and write mode at the time RandomAccessFile was constructed
FileChannel f1 = new FileInputStream("data.txt").getChannel();
FileChannel f2 = new FileOutputStream("data.txt").getChannel();
FileChannel f3 = new RandomAccessFile("data.txt"."rw").getChannel();
Copy the code

Read the content

The channel of the file can be obtained via FileInputStream, and the data is written to ByteBuffer using the read method. The return value of the read method is the number of bytes read, and -1 is returned if the end of the file is read. You can tell if the read is complete by the return value of the read method.

public static void main(String[] args) throws IOException {
        FileChannel f1 = new FileInputStream("data.txt").getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(10000);
        int value = 0;
        while((value = f1.read(buffer))>0)
        {
            System.out.println("\n============="+value);
            buffer.flip();
            while(buffer.hasRemaining())
                System.out.print(((char) buffer.get()));
            buffer.flip();
        }
        f1.close();
    }
Copy the code

Write content

You can get the file’s channel via FileOutputStream and write the buffer to the specified file using the write method. Note that since channels also have sizes, Therefore, the write method does not guarantee that all the contents of the buffer will be written to a channel at once. Write must follow the following rules:

While (buffer.hasRemaining()) {channel.write(buffer); // Use the hasRemaining() method to check whether there is any data in the buffer that is not written to the channel. }Copy the code

Second, always use buffer’s flip method to convert the mode of buffer, otherwise it will write garbled characters.

public static void main(String[] args) throws IOException {
        FileChannel f1 = new FileOutputStream("data2.txt").getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(100);
        buffer.put("hello wolrd!".getBytes(StandardCharsets.UTF_8));
        buffer.flip();
        while(buffer.hasRemaining())
        {
            f1.write(buffer);
        }
        f1.close();
    }
Copy the code

Shut down

Channels need to be closed, and are usually closed by try-with-resource. It is best to obtain the stream and channel using the following method to avoid any reason for the resource not to be closed

public static void main(String[] args) throws IOException { try (FileInputStream fis = new FileInputStream("stu.txt"); FileOutputStream fos = new FileOutputStream("student.txt"); FileChannel inputChannel = fis.getChannel(); FileChannel outputChannel = fos.getChannel()) {// Perform the corresponding operation}}Copy the code

The location of the FileChannel

Buffer has a variable position that indicates the index of the next read/write position. You have a property position in the FileChannel that holds the position where the data is read

long pos = channel.position();
Copy the code

Position (int pos) can be used to set the value of position in a channel, if the current position is set to the end of the file

  • The read will return -1
  • If position goes beyond the end of the file, there will be a void (00) between the new content and the end of the file.

Compulsory to write

For performance reasons, the operating system caches data, not immediately writing it to disk, but writing all data to disk once the cache is full. You can call the force(true) method to write file contents and metadata (information such as file permissions) to disk immediately.

Two FileChannel transfer data

TransferTo method

The transferTo method can be used to transfer data from one channel to another channel quickly and efficiently, but only 2G content can be transferred at a time. TransferTo takes three parameters: position, transfer size, and destination channel

public static void main(String[] args) throws IOException {
        try(
                FileChannel f1 = new FileInputStream("data.txt").getChannel();
                FileChannel f2 = new FileOutputStream("data2.txt").getChannel();) { System.out.println("Size before transmission: F1 size:"+f1.size()+"  f2大小: "+f2.size());
            f1.transferTo(0,f1.size(),f2);
            System.out.println("Size after transmission: F1 size:"+f1.size()+"  f2大小: "+f2.size());
        }catch (IOException e) { }
    }
Copy the code

Results show:

Transmission over 2G

The transferTo method can only transfer content of less than 2G at a time, but the content of more than 2G can be repeatedly transferred in a loop until the transfer is complete:

try(
       FileChannel f1 = new FileInputStream("data.txt").getChannel();
        FileChannel f2 = new FileOutputStream("data2.txt").getChannel();) {long size = f1.size();
    long capacity = f1.size();
    while(capacity>0)
    {
        longthisTransfor = f1.transferTo(size - capacity , capacity, f2); capacity -= thisTransfor; }}catch (IOException e) { }
Copy the code

The Path and the Paths

  • Path indicates the file Path
  • Paths is the utility class for getting Path instances
Path source = Paths.get("1.txt"); // The relative path does not contain a drive letter. Use the user.dir environment variable to locate 1.txt

Path source = Paths.get("d:\\1.txt"); // The absolute path represents the d:\1.txt backslash that needs to be escaped

Path source = Paths.get("d:/1.txt"); // The absolute path also represents d:\1.txt

Path projects = Paths.get("d:\\data"."projects"); // splice represents d:\data\projects
Copy the code

Files

To find the

You can use the Files utility class to check for the existence of Files

Path path = Paths.get("data.txt");
System.out.println(Files.exists(path));
Copy the code

create

If you want to create a level 1 directory:

Path path = Paths.get("study/java");
Files.createDirectory(path);
Copy the code

Two points need to be noted

  • If the directory already exists, will throw exception FileAlreadyExistsException
  • Do not create a multi-level directory at one time. Otherwise, NoSuchFileException will be thrown

To create a multilevel directory:

Path path = Paths.get("study/java/nio");
Files.createDirectories(path);
Copy the code

Copy mobile

Copy Files can use the copy function in the class Files tool, it is important to note that if the file already exists, will throw exceptions FileAlreadyExistsException

Path source = Paths.get("java/data.txt");
Path target = Paths.get("java/data1.txt");
Files.copy(source, target);
Copy the code

If you want to override target with source, you need to use StandardCopyOption to control this

Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Copy the code

Move Files can use Files tool move function in the class, if the third parameter StandardCopyOption. ATOMIC_MOVE guarantee Files mobile atomicity

Path source = Paths.get("java/data.txt");
Path target = Paths.get("study/data.txt");
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
Copy the code

delete

To delete a file, use the delete method in the Files utility class. Note that NoSuchFileException will be thrown if the file does not exist.

Path target = Paths.get("study/data.txt");
Files.delete(target);
Copy the code

Delete the directory and delete Files, all is to use the delete method in the class Files tool, but it is important to note that if the directory and content, will throw exceptions DirectoryNotEmptyException, so can only delete empty directory, if you want to delete is not empty, You can go through all the files in the directory and delete those files and subdirectories, and then delete the directory. However, the Files utility class also provides a more convenient method, the walkFileTree method, described in the next section.

Path target = Paths.get("study/java");
Files.delete(target);
Copy the code

The walkFileTree method

The walkFileTree(Path, FileVisitor) method in the Files utility class makes it easier to manipulate subdirectories and Files in a directory by passing in two parameters

  • Path: indicates the start Path of the file

  • FileVisitor: a file accessor that uses the visitor pattern. There are four methods that can pass in a new SimpleFileVisitor and override the class:

    • PreVisitDirectory: Operations performed before accessing a directory
    • VisitFile: Operations for accessing files
    • VisitFileFailed: Operations performed when a file fails to be accessed
    • PostVisitDirectory: Operations performed after a directory is accessed

Statistics by walkFileTree

Use walkFileTree to count the number of all directories and files in a directory:

public static void main(String[] args) throws IOException {
        Path path = Paths.get("E:\\ppt\\");
        
        Files.walkFileTree(path, new SimpleFileVisitor<Path>(){
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                dicAccount.incrementAndGet();
                System.out.println("= = = >"+dir);
                return super.preVisitDirectory(dir, attrs);
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                fileAccount.incrementAndGet();
                File f = new File(file.toString());
                Files.delete(file);
                return super.visitFile(file, attrs);
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {

                return super.visitFileFailed(file, exc);
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                System.out.println(dir+"< = = =");
                Files.delete(dir);
                return super.postVisitDirectory(dir, exc); }}); System.out.println("dicAccount " + dicAccount.get());
        System.out.println("fileAccount "+ fileAccount.get());
    }
Copy the code

Results show:

The walkFileTree method can also delete all Files and subdirectories in a directory, but it is important to note that the files. delete method can only delete empty directories, so you need to delete Files in visiteFile first. Then delete the subdirectory in the postVisitDirecory method.

Copying multi-level directories

Files.walk(Paths.get(source)).forEach(path1 -> {
            try{
                String name = path1.toString().replace(source,target);
                if(Files.isDirectory(path1))
                {
                    Files.createDirectories(Paths.get(name));
                }
                else if(Files.isRegularFile(path1)) { Files.copy(path1,Paths.get(target)); }}catch(IOException e) { e.printStackTrace(); }});Copy the code