삶 가운데 남긴 기록 AACII.TISTORY.COM
NIO 파일 채널 본문
java.nio.channels.FileChannel
파일 채널을 통해 파일 읽기 쓰기를 할수 있습니다.
동기화 처리가 되어 있어서 멀티 스레드 환경에 안전하게 사용할 수 있습니다.
파일 생성 및 쓰기
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class FileChannelWriteEx {
public static void main(String[] args) throws IOException{
Path path = Paths.get("d:/temp/file.txt");
Files.createDirectories(path.getParent());
//쓰기용으로 파일 채널 생성
FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
String data = "파일 생성 테스트";
Charset charset = Charset.defaultCharset();
ByteBuffer byteBuffer = charset.encode(data);
int byteCount = fileChannel.write(byteBuffer);
System.out.println("file.txt : "+ byteCount+ " 바이트 씀");
fileChannel.close();
}
}
파일 읽기
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class FileChannelReadEx {
public static void main(String[] args) throws IOException{
Path path = Paths.get("d:/temp/file.txt");
Files.createDirectories(path.getParent());
//읽기용으로 파일 채널 생성
FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ);
ByteBuffer byteBuffer = ByteBuffer.allocate(200);
Charset charset = Charset.defaultCharset();
String data = "";
int byteCount;
while(true) {
byteCount = fileChannel.read(byteBuffer); //파일로부터 데이터 읽기
if(byteCount == -1) break;
byteBuffer.flip();
data += charset.decode(byteBuffer).toString();
byteBuffer.clear();
}
fileChannel.close();
System.out.println(data);
}
}
파일 복사
파일 복사를 구현하려면 ByteBuffer를 소스파일과 타겟 파일 사이에 두고 읽기와 쓰기를 교대로 번갈아가면서 수행하면 됩니다.
채널과 채널 사이의 버퍼는 direct buffer가 성능이 종습니다.
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class FileCopyEx {
public static void main(String[] args) throws IOException {
//해당경로에 해당 파일이 있다는 가정
Path from = Paths.get("src/net/aacii/nio/color.jpg"); //원본 소스 파일
Path to = Paths.get("src/net/aacii/nio/color_copy.jpg"); //복사할 타겟 파일
//읽기 FileChannel 생성
FileChannel fileChannel_from = FileChannel.open(from, StandardOpenOption.READ);
//쓰기 FileChannel 생성
FileChannel fileChannel_to = FileChannel.open(to, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocateDirect(100);
int byteCount;
while(true) {
buffer.clear();
byteCount = fileChannel_from.read(buffer); //읽기
if(byteCount == -1) break;
buffer.flip();
fileChannel_to.write(buffer);
}
fileChannel_from.close();
fileChannel_to.close();
System.out.println("파일 복사 상공");
}
}
단순히 파일만 복사하는 것이라면 Files 클래스의 copy() 메소드를 이용하는 것이 간단합니다.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
public class FileCopyMethodEx {
public static void main(String[] args) throws IOException {
Path from = Paths.get("src/net/aacii/nio/color.jpg"); //원본 소스 파일
Path to = Paths.get("src/net/aacii/nio/color_copy.jpg"); //복사할 타겟 파일
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
System.out.println("파일 복사 성공");
}
}
파일 비동기 채널
FileChannel의 read()와 write()는 파일 입출력 작업동안 블로킹됩니다.
NIO에서는 불특정 다수 파일 및 대용량 파일 처리를 위해 비동기 파일 채널(AsynchronousFileChannel)을 제공합니다.
비동기로 파일 쓰기
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AsyncFileChannelEx {
public static void main(String[] args) throws IOException, InterruptedException {
//스레드 풀 생성
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
for(int i=0; i<10; i++) {
Path path = Paths.get("d:/temp/file" + i + ".txt");
Files.createDirectories(path.getParent());
//비동기 파일 채널 생성
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE), executorService);
//파일에 저장할 데이터를 ByteBuffer에 저장
Charset charset = Charset.defaultCharset();
ByteBuffer byteBuffer = charset.encode("안녕!");
//첨부 객체 생성
class Attachment{
Path path; //파일 경로 저장
AsynchronousFileChannel fileChannel; //비동기 파일 채널 저장
}
Attachment attachment = new Attachment();
attachment.path = path;
attachment.fileChannel = fileChannel;
//CompletionHandler (콜백 기능이 구현된 객체)
CompletionHandler<Integer, Attachment> completionHandler = new CompletionHandler<Integer, Attachment>(){
@Override
public void completed(Integer result, Attachment attachment) {
System.out.println(attachment.path.getFileName()+ " : "+ result + "바이트 쓰기 : "+Thread.currentThread().getName());
try {
attachment.fileChannel.close();
} catch(IOException e) {
}
}
@Override
public void failed(Throwable exc, Attachment attachment) {
exc.printStackTrace();
try {
attachment.fileChannel.close();
}catch(IOException e) {
}
}
};
fileChannel.write(byteBuffer, 0, attachment, completionHandler);
}
//스레드풀 종료
Thread.sleep(1000);
executorService.shutdown();
}
}
비동기로 파일 읽기
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AsyncFileChannelEx2 {
public static void main(String[] args) throws IOException, InterruptedException {
//스레드 풀 생성
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
for(int i=0; i<10; i++) {
Path path = Paths.get("d:/temp/file" + i + ".txt");
Files.createDirectories(path.getParent());
//비동기 파일 채널 생성
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, EnumSet.of(StandardOpenOption.READ), executorService);
//파일 크기와 동일한 capacity를 갖는 버퍼 생성
ByteBuffer byteBuffer = ByteBuffer.allocate((int)fileChannel.size());
//첨부 객체 생성
class Attachment{
Path path; //파일 경로 저장
AsynchronousFileChannel fileChannel; //비동기 파일 채널 저장
ByteBuffer byteBuffer;
}
Attachment attachment = new Attachment();
attachment.path = path;
attachment.fileChannel = fileChannel;
attachment.byteBuffer = byteBuffer;
//CompletionHandler (콜백 기능이 구현된 객체)
CompletionHandler<Integer, Attachment> completionHandler = new CompletionHandler<Integer, Attachment>(){
@Override
public void completed(Integer result, Attachment attachment) {
attachment.byteBuffer.flip();
//버퍼에 저장된 데이터를 문자열로 복원
Charset charset = Charset.defaultCharset();
String data = charset.decode(attachment.byteBuffer).toString();
System.out.println(attachment.path.getFileName() + " : "+ data + " : "+Thread.currentThread().getName());
try {
attachment.fileChannel.close();
} catch(IOException e) {
}
}
@Override
public void failed(Throwable exc, Attachment attachment) {
exc.printStackTrace();
try {
attachment.fileChannel.close();
}catch(IOException e) {
}
}
};
//파일 읽기
//다른 스레드 작업때문에 여기서 FileChannel을 close()하는게 아니라 완료/실패시 콜백 후 close()를 호출해야 합니다.
fileChannel.read(byteBuffer, 0, attachment, completionHandler);
}
//스레드풀 종료
executorService.shutdown();
}
}
728x90
'DEV&OPS > Java' 카테고리의 다른 글
NIO TCP Non-Blocking Channel (0) | 2022.05.26 |
---|---|
NIO TCP 서버/클라이언트 채팅 프로그램 (0) | 2022.05.26 |
NIO Buffer (0) | 2022.05.24 |
NIO Path (0) | 2022.05.23 |
IO(블로킹)기반 자바 네트워킹 (0) | 2022.05.20 |