参考文献

img

BIO NIO和AIO

  • BIO、NIO和AIO是Java中用于网络编程的三种不同的I/O模型,它们之间的区别主要体现在对IO操作的处理方式和API设计上.以下是它们的区别:

BIO(Blocking I/O)阻塞I/O

  • 同步阻塞:每个连接都需要一个独立的线程来处理,因此连接数受限于服务器的性能.
  • 通信是同步的,即代码阻塞在输入流(InputStream)和输出流(OutputStream)上等待I/O完成.
  • 只有阻塞模式,无法发挥多路复用的优势.
  • 在I/O通信线程和业务处理线程中使用同一个线程池,无法发挥多核CPU的优势.

NIO(Non-Blocking I/O)非阻塞I/O

  • 异步非阻塞:NIO可以使用单线程处理多个连接(轮询器Selector),因此可以支持更多的连接.
  • 通过Selector实现了多路复用,同时可以采取异步非阻塞模式,即不用等待I/O操作的完成,可以通过回调函数的方式进行处理.
  • 采用了“缓冲区”来处理数据,数据读入缓冲区,再从缓冲区读取、处理.
  • 同步阻塞模式可以通过配置为同步非阻塞模式实现.

AIO(Asynchronous I/O)异步I/O

  • 异步非阻塞:异步的I/O操作不仅仅可以异步读取数据,同时也可以异步地向客户端发送数据.
  • 采用异步的方式进行I/O操作,I/O操作的完成后将通过回调函数来通知应用程序,使得应用程序可以继续做一些其他的事情,而不必等待I/O操作的完成.
  • AsynchronousFileChannel: 用于文件异步读写;
  • AsynchronousSocketChannel: 客户端异步socket;
  • AsynchronousServerSocketChannel: 服务器异步socket。

总结

  • 总的来说,BIO适用于连接数比较小的情况,NIO适用于连接数比较多的情况,AIO适用于I/O操作比较频繁的情况.不同的I/O模型在不同的场景下应用,可以提高网络通信的效率,提升应用程序的吞吐量.

Java NIO

  • NIO库是JDK1.4引入的,NIO弥补了原来同步阻塞I/O的不足

NIO和传统I/O的区别

  • 传统I/O是一次一个字节的处理数据,NIO是以块(缓冲区)的形式处理数据.
  • 最主要的是,NIO可以实现非阻塞,而传统I/O只能阻塞;
  • I/O的实际场景是文件I/O和网络I/O,NIO在网络I/O场景下提升就是尤其明显;

NIO的组成部分(三个部分)

  • Buffer(缓冲区): Buffer是存储数据的地方;
  • Channel(管道): Channel是运输数据的载体;
  • Selector(选择器): Selector用于检查多个Channel的状态变更情况;

Buffer(缓冲区)

  • Buffer是一个对象,它包含一些要写入或者要读出的数据.在面向流的I/O中,可以将数据直接写入或者将数据直接读到Stream对象中.
  • 在NIO库中,所有数据都是用缓冲区处理的.在读取数据时,它是直接读到缓冲区中的;在写入数据时,写入到缓冲区中.任何时候访问NIO中的数据,都是通过缓冲区进行操作.
  • 缓冲区实质上是一个数组.通常它是一个字节数组(ByteBuffer),也可以使用其他种类的数组.但是一恶搞缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置(limit)等信息.

Channel(通道)

  • Channel是一个通道,它就像自来水管一样,网络数据通过Channel读取和写入.通道与流的不同之处在于通道是双向的,流只是在一个方向上移动(一个流必须是InputStream或者OutputStream的子类),而通道可以用于读,写或者二者同时进行.
  • Channel可以分为两大类:
    • 用于网络读写的SelectableChannel
      • SocketChannel:用于TCP网络连接的读写操作;
      • ServerSocketChannel:用于监听TCP连接请求;
      • DatagramChannel:用于UDP网络连接的读写操作。
    • 用于文件操作的FileChannel,它可以在文件中读取和写入数据,并支持文件锁定操作。

Selector(多路复用器)

  • 多路复用器提供选择已经就绪的任务的能力.即Selector会不断地轮询注册在其上的Channel,如果某个Channel上面发生读或者写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的I/O操作.

FileChannel

  • FileChannel只能工作在阻塞模式下;

获取FileChannel的方式

  • 不能直接打开 FileChannel,必须通过 FileInputStreamFileOutputStream 或者 RandomAccessFile 来获取 FileChannel,它们都有 getChannel 方法

    • 通过 FileInputStream 获取的 channel只能读

      1
      2
      3
      File file = new File("example.txt");
      FileOutputStream fos = new FileOutputStream(file);
      FileChannel channel = fos.getChannel();
    • 通过 FileOutputStream 获取的 channel 只能写

      1
      2
      3
      File file = new File("example.txt");
      FileInputStream fis = new FileInputStream(file);
      FileChannel channel = fis.getChannel();
    • 通过 RandomAccessFile 是否能读写根据构造 RandomAccessFile 时的读写模式决定

      1
      2
      3
      File file = new File("example.txt");
      RandomAccessFile raf = new RandomAccessFile(file, "rw");
      FileChannel channel = raf.getChannel();
  • 无论哪种方式,都需要先创建一个File对象来代表要操作的文件,然后再通过相应的输入/输出流或RandomAccessFile对象获取FileChannel

写入方式

  • 写入的正确姿势

    1
    2
    3
    4
    5
    6
    7
    ByteBuffer buffer = ...;
    buffer.put(...); // 存入数据
    buffer.flip(); // 切换读模式

    while(buffer.hasRemaining()) {
    channel.write(buffer);
    }
  • 在 while 中调用 channel.write 是因为 write 方法并不能保证一次将 buffer 中的内容全部写入 channel

关闭

  • channel 必须关闭,不过调用了 FileInputStream、FileOutputStream 或者 RandomAccessFile 的 close 方法会间接地调用 channel 的 close 方法

NIO操作文件代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
package com.holelin.sundry.test.io;

import com.google.common.io.ByteStreams;
import com.google.common.io.LineProcessor;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.io.Writer;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* @Description:
* @Author: HoleLin
* @CreateDate: 2022/7/27 18:21
* @UpdateUser: HoleLin
* @UpdateDate: 2022/7/27 18:21
* @UpdateRemark: 修改内容
* @Version: 1.0
*/
public class NIOTest {

public static final String SMALL_FILE_PATH = "/Users/holelin/Downloads/手术-示例报文.txt";
public static final String LARGE_FILE_PATH = "/Users/holelin/Downloads/win11-22000-arm64.ISO";
public static final String DEMO_FILE_PATH = "/Users/holelin/Downloads/demo.txt";
public static final String RENAME_FILE_PATH = "/Users/holelin/Downloads/demo2.txt";
public static final String MOVE_FILE_PATH = "/Users/holelin/demo2.txt";
public static final String READ_ONLY_FILE_PATH = "/Users/holelin/Downloads/read_only.txt";
public static final String fileName = "bootstrap.yml";

public static final String DIR = "/Users/holelin/Downloads";

@Test
public void readSmallFile() throws IOException {
try (RandomAccessFile aFile = new RandomAccessFile(SMALL_FILE_PATH, "r");
FileChannel inChannel = aFile.getChannel();) {

long fileSize = inChannel.size();

//Create buffer of the file size
ByteBuffer buffer = ByteBuffer.allocate((int) fileSize);
inChannel.read(buffer);
buffer.flip();

// Verify the file content
for (int i = 0; i < fileSize; i++) {
System.out.print((char) buffer.get());
}
} catch (IOException e) {
e.printStackTrace();
}
}

@Test
public void readLargeFile() {
try (RandomAccessFile aFile = new RandomAccessFile(SMALL_FILE_PATH, "r");
FileChannel inChannel = aFile.getChannel();) {

//Buffer size is 1024
ByteBuffer buffer = ByteBuffer.allocate(1024);

while (inChannel.read(buffer) > 0) {
buffer.flip();
for (int i = 0; i < buffer.limit(); i++) {
System.out.print((char) buffer.get());
}
buffer.clear(); // do something with the data and clear/compact it.
}
} catch (IOException e) {
e.printStackTrace();
}
}

@Test
public void readUsingMappedByteBuffer() {
try (RandomAccessFile aFile = new RandomAccessFile(SMALL_FILE_PATH, "r");
FileChannel inChannel = aFile.getChannel();) {

MappedByteBuffer buffer = inChannel
.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());

buffer.load();
for (int i = 0; i < buffer.limit(); i++) {
System.out.print((char) buffer.get());
}
buffer.clear(); // do something with the data and clear/compact it.

} catch (IOException e) {
e.printStackTrace();
}
}

@Test
public void writeUsingChannel() {
Path filePath = Path.of("demo.txt");
String content = "hello world !!";
try (
RandomAccessFile stream = new RandomAccessFile(filePath.toFile(), "rw");
FileChannel channel = stream.getChannel();) {

byte[] strBytes = content.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(strBytes.length);

buffer.put(strBytes);
buffer.flip();
channel.write(buffer);
} catch (Exception e) {

}
}

@Test
public void writeUsingFileOutputStream() {
Path filePath = Path.of("demo.txt");
String content = "hello world !!1";

try (FileOutputStream outputStream
= new FileOutputStream(filePath.toFile())) {

byte[] strToBytes = content.getBytes();
outputStream.write(strToBytes);
} catch (Exception e) {

}
}

@Test
public void outputStreamToInputStream() {
try (FileOutputStream fos = new FileOutputStream(new File("/Users/holelin/Projects/MySelf/Java-Notes/sundry/demo.txt"));
FileInputStream fis = new FileInputStream(new File("/Users/holelin/Projects/MySelf/Java-Notes/sundry/demo2.txt"));) {

FileChannel outputChannel = fos.getChannel();
FileChannel inputChannel = fis.getChannel();

outputChannel.transferTo(0, inputChannel.size(), inputChannel);
} catch (Exception e) {

}
}

@Test
public void channelTransfer() throws IOException {
//Input files
String[] inputFiles = new String[]{"demo.txt", "demo2.txt", "demo3.txt"};

//Files contents will be written in these files
String outputFile = "outputFile.txt";

//Get channel for output file
FileOutputStream fos = new FileOutputStream(new File(outputFile));
WritableByteChannel targetChannel = fos.getChannel();

for (int i = 0; i < inputFiles.length; i++) {
//Get channel for input files
FileInputStream fis = new FileInputStream(inputFiles[i]);
FileChannel inputChannel = fis.getChannel();

//Transfer data from input channel to output channel
inputChannel.transferTo(0, inputChannel.size(), targetChannel);

//close the input channel
inputChannel.close();
fis.close();
}

//finally close the target channel
targetChannel.close();
fos.close();

}

@Test
public void createFile() throws IOException {
// crate
final Path path = Paths.get(DEMO_FILE_PATH);
if (!path.toFile().exists()) {
Files.createFile(path);
}
// create read only file
final Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("r--r--r--");
final FileAttribute<Set<PosixFilePermission>> attribute = PosixFilePermissions.asFileAttribute(permissions);
final Path readOnlyPath = Paths.get(READ_ONLY_FILE_PATH);
if (!readOnlyPath.toFile().exists()) {
Files.createFile(readOnlyPath, attribute);
}
}

@Test
public void readFileToByte() throws IOException {
// Java 8
final Path path = Paths.get(SMALL_FILE_PATH);
final byte[] bytes = Files.readAllBytes(path);

// Java 6
final File file = new File(SMALL_FILE_PATH);
FileInputStream fileInputStream = null;
final byte[] bfile = new byte[(int) file.length()];
try {
fileInputStream = new FileInputStream(file);
fileInputStream.read(bfile);
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
// Apache Commons IO
//Using FileUtils.readFileToByteArray()
final byte[] bytes1 = FileUtils.readFileToByteArray(file);
//Using IOUtils.toByteArray
final byte[] bytes2 = IOUtils.toByteArray(new FileInputStream(file));

// Guava
final byte[] bytes3 = com.google.common.io.Files.toByteArray(file);
final byte[] bytes4 = ByteStreams.toByteArray(new FileInputStream(file));
}

@Test
public void readLines() {
final Path path = Paths.get(SMALL_FILE_PATH);
try (final Stream<String> lines = Files.lines(path)) {
final List<String> filteredLines = lines.filter(s -> s.contains("body")).collect(Collectors.toList());
filteredLines.forEach(System.out::println);
} catch (IOException e) {
throw new RuntimeException(e);
}


File file = new File(SMALL_FILE_PATH);
try (FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}


try {
List<String> lines = com.google.common.io.Files.readLines(file,
Charset.defaultCharset());
} catch (IOException e) {
e.printStackTrace();
}

// With LineProcessor
LineProcessor<List<String>> lineProcessor = new LineProcessor<>() {
final List<String> result = new ArrayList<>();

@Override
public boolean processLine(final String line) throws IOException {
result.add(StringUtils.capitalize(line));
return true; // keep reading
}

@Override
public List<String> getResult() {
return result;
}
};

try {
List<String> lines = com.google.common.io.Files
.asCharSource(file, Charset.defaultCharset())
.readLines(lineProcessor);
} catch (IOException e) {
e.printStackTrace();
}

try {
List<String> lines = FileUtils.readLines(file, Charset.defaultCharset());
} catch (IOException e) {
e.printStackTrace();
}
}

@Test
public void readFileFromClassPath() throws FileNotFoundException {

final ClassLoader classLoader = getClass().getClassLoader();
final URL resource = classLoader.getResource("bootstrap.yml");
if (Objects.isNull(resource)) {
throw new FileNotFoundException("file is not found!");
} else {
final File file = new File(resource.getFile());
try {
List<String> lines = FileUtils.readLines(file, Charset.defaultCharset());
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}

@Test
public void readFileFromResourceDirectory() {
final InputStream is = this.getClass().getClassLoader()
.getResourceAsStream(fileName);
try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}

final URL resource = this.getClass().getClassLoader().getResource(fileName);
final File file = new File(resource.getFile());
}


@Test
public void writeFile() {
// fast writing fileChannel and bytebuffer
Path filePath = Path.of(DEMO_FILE_PATH);
String content = "hello world !!";
try (
RandomAccessFile stream = new RandomAccessFile(filePath.toFile(), "rw");
FileChannel channel = stream.getChannel();) {

byte[] strBytes = content.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(strBytes.length);

buffer.put(strBytes);
buffer.flip();
channel.write(buffer);
} catch (Exception e) {

}

content = "hello world !!2";
try (BufferedWriter writer = new BufferedWriter(
new FileWriter(filePath.toFile()))) {
writer.write(content);
} catch (Exception e) {

}

// Using FileOutputStream
try (FileOutputStream outputStream
= new FileOutputStream(filePath.toFile())) {
byte[] strToBytes = content.getBytes();
outputStream.write(strToBytes);
} catch (IOException e) {
throw new RuntimeException(e);
}

// Using DataOutputStream
try (FileOutputStream outputStream
= new FileOutputStream(filePath.toFile());
DataOutputStream dataOutStream
= new DataOutputStream(new BufferedOutputStream(outputStream));) {
dataOutStream.writeUTF(content);
dataOutStream.writeInt(10);
dataOutStream.writeLong(100L);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Test
public void appendToFile() throws IOException {
// Using NIO Files
String textToAppend = "Happy Learning !!";
Path path = Paths.get(DEMO_FILE_PATH);
Files.write(path, textToAppend.getBytes(), StandardOpenOption.APPEND);

// Using BufferedWriter
try (FileWriter fw = new FileWriter(DEMO_FILE_PATH, true);
BufferedWriter writer = new BufferedWriter(fw);) {

writer.write(textToAppend);
}

// Using FileOutputStream
try (FileOutputStream outputStream
= new FileOutputStream(fileName, true)) {

byte[] strToBytes = textToAppend.getBytes();
outputStream.write(strToBytes);
}
}

@Test
public void writeFileWithEncode() {
// Writing UTF-8 Encoded Data into a File
try (Writer out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(DEMO_FILE_PATH), StandardCharsets.UTF_8))) {

out.append("Test")
.append("\r\n")
.append("UTF-8 Demo")
.append("\r\n")
.append("冲冲冲")
.append("\r\n");

out.flush();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}

@Test
public void moveAndRename() throws IOException {

// File.renameTo()
File originalFile = new File(DEMO_FILE_PATH);
File renamedFile = new File(RENAME_FILE_PATH);
File movedFile = new File(MOVE_FILE_PATH);
final boolean b = originalFile.renameTo(renamedFile);
final boolean b1 = renamedFile.renameTo(movedFile);

// NIO Files.move
final Path path = Path.of(DEMO_FILE_PATH);
// rename in same directory
Files.move(path, path.resolveSibling("test.txt"));

// FileUtils
FileUtils.moveFile(originalFile, renamedFile);
File targetDirectory = new File(DEMO_FILE_PATH);
FileUtils.moveFileToDirectory(originalFile, targetDirectory, true);
}

@Test
public void copyFile() throws IOException {
// Files.copy
final Path source = Paths.get(DEMO_FILE_PATH);
final Path target = Paths.get(source.resolveSibling("test.txt").toString());
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);

// FileChannel.transferTo()
final File file = source.toFile();
final FileInputStream sourceInputStream = new FileInputStream(file);
final FileChannel inChannel = sourceInputStream.getChannel();
final FileOutputStream targetOutputStream = new FileOutputStream(target.toFile());
final FileChannel outChannel = targetOutputStream.getChannel();

inChannel.transferTo(0, file.length(), outChannel);
sourceInputStream.close();
targetOutputStream.close();

}

@Test
public void deleteFile() throws IOException {
final Path path = Paths.get(DEMO_FILE_PATH);
final File file = path.toFile();

// java.nio.file.Files
Files.delete(path);
Files.deleteIfExists(path);

// org.apache.commons.io.FileUtils
FileUtils.delete(file);
FileUtils.deleteQuietly(file);
FileUtils.deleteDirectory(new File("/Users/holelin/Downloads/a"));
}

@Test
public void fileSize() throws IOException {
final Path path = Paths.get(DEMO_FILE_PATH);
final long size = Files.size(path);
System.out.println(size);
final long sizeOf = FileUtils.sizeOf(path.toFile());
System.out.println(FileUtils.byteCountToDisplaySize(sizeOf));
}

@Test
public void readDir() throws IOException {
Path path = Paths.get(DIR);
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);
System.out.println(fileCount);
}
/**
* 递归删除文件夹,即使文件夹不为空
*
* @param dirPath 文件夹目录
*/
public static void recursionDeleteDirectory(Path dirPath) throws IOException {
Assert.isTrue(Objects.nonNull(dirPath), "目录不能为空");
Assert.isTrue(dirPath.toFile().isDirectory(), "路径不为目录");
Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
// 先去遍历删除文件
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
Files.delete(file);
log.debug("文件被删除:{}", file);
return FileVisitResult.CONTINUE;
}

// 再去遍历删除目录
@Override
public FileVisitResult postVisitDirectory(Path dir,
IOException exc) throws IOException {
Files.delete(dir);
log.debug("文件夹被删除:{}", dir);
return FileVisitResult.CONTINUE;
}
}
);
}
}

两个Channel传输数据

1
2
3
4
5
6
7
8
9
10
11
12
String FROM = "helloword/data.txt";
String TO = "helloword/to.txt";
long start = System.nanoTime();
try (FileChannel from = new FileInputStream(FROM).getChannel();
FileChannel to = new FileOutputStream(TO).getChannel();
) {
from.transferTo(0, from.size(), to);
} catch (IOException e) {
e.printStackTrace();
}
long end = System.nanoTime();
System.out.println("transferTo 用时:" + (end - start) / 1000_000.0);

超过2G大小的文件传输

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
left -= from.transferTo((size - left), left, to);
}
} catch (IOException e) {
e.printStackTrace();
}

Files.list()/Files.walk()/Files.walkFileTree()

方法 功能 递归层级 返回类型 适用场景
Files.list(...) 列出目录的文件和子目录 不递归 Stream<Path> 只需列出目录中直接文件的简单场景
Files.walk(...) 递归遍历目录 可控制深度 Stream<Path> 递归遍历目录,但只需要处理每个文件或目录的简单场景
Files.walkFileTree(...) 递归遍历目录 完全递归 / 需要对遍历过程进行精细控制和复杂操作的场景