This is the 8th day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021.

This article focuses on the use of files in NIO to become, primarily, FileChannel.

Common FileChannel operations

1.1 get FileChannel

There is a file called text.txt with the following contents:

abcdef
Copy the code

You cannot open A FileChannel directly; you must obtain a FileChannel via FileInputStream, FileOutputStream, or RandomAccessFile, all of which have the getChannel method

1.1.1 Obtaining the file through FileInputStream

    public static void main(String[] args) throws Exception {
        // Get a channel using FileInputStream
        FileInputStream fileInputStream = new FileInputStream(new File("C:\\Users\\P50\\Desktop\\text.txt"));
        FileChannel channel1 = fileInputStream.getChannel();
        ByteBuffer buffer= ByteBuffer.allocate(10);
        //channel1.write(buffer);
        channel1.read(buffer);
        buffer.flip();
        System.out.println((print(buffer)));
    }

    static String print(ByteBuffer b) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < b.limit(); i++) {
            stringBuilder.append((char) b.get(i));
        }
        return stringBuilder.toString();
    }
Copy the code

Results:

abcdef
Copy the code

Channels obtained by FileInputStream are only readable. If the write write method is used, an exception will be thrown:

Exception in thread "main" java.nio.channels.NonWritableChannelException
	at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:201)
	at com.cloud.bssp.nio.FileChannel.GetFileChannel.main(GetFileChannel.java:21)
Copy the code

1.1.2 Obtaining the file through FileOutputStream

    public static void main(String[] args) throws Exception {
        // Use FileOutputStream to get a channel
        FileOutputStream fileOutputStream = new FileOutputStream(new File("C:\\Users\\P50\\Desktop\\text.txt"),true);
        FileChannel channel2 = fileOutputStream.getChannel();
        ByteBuffer buffer= ByteBuffer.allocate(10);
        buffer.put(StandardCharsets.UTF_8.encode("helloworld"));
        buffer.flip();
        channel2.write(buffer);
    }

    static String print(ByteBuffer b) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < b.limit(); i++) {
            stringBuilder.append((char) b.get(i));
        }
        return stringBuilder.toString();
    }
Copy the code

The file is written to. Note that the FileOutputStream property append, if true, appends, otherwise overwrites. The append used in this article.

abcdefhelloworld
Copy the code

Channels obtained via FileOutputStream can only be written. If the read method is used, an exception will be thrown:

Exception in thread "main" java.nio.channels.NonReadableChannelException
	at sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:149)
	at com.cloud.bssp.nio.FileChannel.GetFileChannel.main(GetFileChannel.java:28)
Copy the code

1.1.3 Obtain through RandomAccessFile

    public static void main(String[] args) throws Exception {
        // Get the channel using RandomAccessFile
        RandomAccessFile file = new RandomAccessFile("C:\\Users\\P50\\Desktop\\text.txt"."rw");
        FileChannel channel3 = file.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(15);
        // Read the contents of the file into buffer
        channel3.read(buffer);
        buffer.flip();
        System.out.println(print(buffer));

        // Switch to write mode and empty buffer
        buffer.clear();
        // Write helloWorld to the file
        buffer.put(StandardCharsets.UTF_8.encode("helloworld"));
        buffer.flip();
        channel3.write(buffer);
    }

        // Switch to write mode and empty buffer
        buffer.clear();
        // Write helloWorld to the file
        buffer.put(StandardCharsets.UTF_8.encode("helloworld"));
        buffer.flip();
        channel3.write(buffer);

Copy the code

Result: One byte is missing because the buffer I specified is only 15, and the document is 16, which is read only once.

abcdefhelloworl
Copy the code

The content of the document is modified as follows, concatenating the content read by the channel with the new content added

abcdefhelloworlhelloworld
Copy the code

Whether we can read and write through RandomAccessFile depends on the read and write mode at the time RandomAccessFile was constructed, specifying the RW (read and write mode).

1.2 Read and Write

1.2.1 read

As shown in the previous fetch example, an int is returned, the ByteBuffer is filled with data read from a channel, and the return value is -1, indicating that the end of the file has been reached.

int readBytes = channel.read(buffer);
Copy the code

Still using the first example above, -1 is returned if the document is empty

 int read = channel1.read(buffer);
 System.out.println(read);
Copy the code
- 1Copy the code

1.2.2 write

The example in the previous section showed how to write data to a buffer using the write method to write to a channel, but the correct way to write data should look like this:

while(buffer.hasRemaining()) {
    channel.write(buffer);
}
Copy the code

HasRemaining () is a method of buffer that determines whether position is less than limit. If it is, it returns true, indicating that buffer still has unread data.

Write is called in the while because the write method does not guarantee that all the contents of the buffer will be written to the channel at once.

1.2.3 Forcible Write

For performance reasons, the operating system caches data instead of writing it to disk immediately. You can call the channel.force(true) method to write the file contents and metadata (information such as the file’s permissions) to disk immediately.

public abstract void force(boolean metaData) throws IOException;
Copy the code

1.3 shut down

None of the code we’ve written above actually closes streams and channels, which can cause serious problems in production.

A channel must be closed, but a close() method calling FileInputStream, FileOutputStream, or RandomAccessFile indirectly calls a channel’s close method.

Take a look at FileInputStream’s close method:

    public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }
        if (channel != null) {
           channel.close();
        }

        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               close0();
           }
        });
    }
Copy the code

1.4 Position of the FileChannel

Get current location

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

Set current Position

longnewPos = ... ; channel.position(newPos);Copy the code

Obtain file channel as follows:

Helloworld RandomAccessFile file = new RandomAccessFile("C:\ Users\ P50\ Desktop\ text.txt", "rw"); FileChannel channel = file.getChannel();Copy the code

Print the position of different Settings:

        // Print position, 0 if not read
        System.out.println(channel.position());

        // Read the length of the file
        ByteBuffer buffer = ByteBuffer.allocate(10);
        channel.read(buffer);
        System.out.println(channel.position());

        // Set the length after the position
        FileChannel position = channel.position(5);
        System.out.println(position.position());
Copy the code

Results:

0 to 10 5Copy the code

1.5 Obtaining the File size

channel.size();
Copy the code

2. Mutual transmission of channels

A channel provides two methods for transferring data between channels:

TransferTo (0, in.size(), out); /** * transfer data from a channel to target, where position, count, are * in.transferto (0, in.size(), out); */ transferTo(long position, long count, WritableByteChannel target)Copy the code
TransferFrom (in,0,in.size()); */ transferFrom(ReadableByteChannel src, long position, long count)Copy the code

The following is an example:

public class TestCopyFileByNIO { public static void fileChannelCopy(String sfPath, String tfPath) { FileInputStream fi = null; FileOutputStream fo = null; FileChannel in = null; FileChannel out = null; try { fi = new FileInputStream(new File(sfPath)); fo = new FileOutputStream(new File(tfPath)); in = fi.getChannel(); // Get the corresponding file channel out = fo.getChannel(); In.transferto (0, in.size(), out); } catch (Exception e) {e.printStackTrace();} catch (Exception e) {e.printstackTrace (); } finally { try { fi.close(); fo.close(); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { long start = System.currentTimeMillis(); String sPath = "E:\\workspace\\comprehend-service.rar"; String tPath = "E:\\workspace\\comprehend-service-" + System.currentTimeMillis() + "-bak.rar"; fileChannelCopy(sPath, tPath); long end = System.currentTimeMillis(); System.out.println(" time: "+ (end-start) + "ms"); }}Copy the code

Results:

Time: 194msCopy the code

2.1 Maximum transmission value of a channel

The transmission size of a channel is limited. The maximum size of a channel is 2 GIGABytes, which will cause data loss. So you need to use loops to transfer data multiple times.

public class TestCopyFileByNIO {

    public static void fileChannelCopy(String sfPath, String tfPath) {
        FileInputStream fi = null;
        FileOutputStream fo = null;
        FileChannel in;
        FileChannel out;
        try {
            fi = new FileInputStream(new File(sfPath));
            fo = new FileOutputStream(new File(tfPath));
            in = fi.getChannel();
            out = fo.getChannel();
            // Total file size
            long size = in.size();
            // left the number of remaining files
            for (long left = size; left > 0;) { System.out.println("position = " + (size - left) + ",left = " + left);
                // transferTo Returns the number transferred. The remaining minus the transferred is the current number remainingleft -= in.transferTo((size -left), left, out); }}catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fi.close();
                fo.close();
            } catch(Exception e) { e.printStackTrace(); }}}public static void main(String[] args) {
        long start = System.currentTimeMillis();
        String sPath = "E:\\workspace\\workspace.zip";
        String tPath = "E:\\workspace\\workspace-" + System.currentTimeMillis() + "-bak.zip";
        fileChannelCopy(sPath, tPath);
        long end = System.currentTimeMillis();
        System.out.println("用时为:" + (end - start) + "ms");
    }
Copy the code

Results:

Position = 0, left = 2925330022 position = 2147483647, left = 777846375Copy the code

Path and Paths

Jdk7 introduces the Path and Paths classes

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

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

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

 // represents d:\data\projects
Path projects = Paths.get("d:\\data"."projects");
Copy the code
  • .Represents the current path
  • .Represents the upper-level path

For example, the directory structure is as follows

d:
	|- data
		|- projects
			|- a
			|- b
Copy the code

code

Path path = Paths.get("d:\\data\\projects\\a\\.. \\b");
System.out.println(path);
// Normalize the path
System.out.println(path.normalize()); 
Copy the code

Will be output

d:\data\projects\a\.. \b d:\data\projects\bCopy the code

Four, Files

Check whether the file exists

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

Creating a Level-1 Directory

Path path = Paths.get("helloword/d1");
Files.createDirectory(path);
Copy the code
  • If the directory already exists, will throw exception FileAlreadyExistsException
  • Do not create a multi-level directory at one time. Otherwise, NoSuchFileException will be thrown

Create a multilevel directory

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

Copy files

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

Files.copy(source, target);
Copy the code
  • If the file already exists, will throw exceptions FileAlreadyExistsException

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

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

Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
Copy the code
  • Move the atomicity of StandardCopyOption. ATOMIC_MOVE guarantee file

Delete the file

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

Files.delete(target);
Copy the code
  • If the file does not exist, NoSuchFileException is thrown

Delete the directory

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

Files.delete(target);
Copy the code
  • If the directory and content, will throw exception DirectoryNotEmptyException

Traverse directory files

public static void main(String[] args) throws IOException {
    Path path = Paths.get("C: \ \ Program Files \ \ Java \ \ jdk1.8.0 _91");
    AtomicInteger dirCount = new AtomicInteger();
    AtomicInteger fileCount = new AtomicInteger();
    Files.walkFileTree(path, new SimpleFileVisitor<Path>(){
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) 
            throws IOException {
            System.out.println(dir);
            dirCount.incrementAndGet();
            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(dirCount);/ / 133
    System.out.println(fileCount); / / 1479
}
Copy the code

Count the number of jars

Path path = Paths.get("C: \ \ Program Files \ \ Java \ \ jdk1.8.0 _91");
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
Copy the code

Deleting a Multi-level Directory

Path path = Paths.get("d:\\a");
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); }});Copy the code

Delete is a dangerous operation, so make sure the folder you want to recursively delete has no important content

Copying multi-level directories

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);
        / / is a directory
        if (Files.isDirectory(path)) {
            Files.createDirectory(Paths.get(targetName));
        }
        // It is a normal file
        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);
Copy the code

NIO file programming here is written here, if there is help friends a thumbs up