Recent Posts
Recent Comments
Link
12-04 00:16
Today
Total
관리 메뉴

삶 가운데 남긴 기록 AACII.TISTORY.COM

NIO 파일 채널 본문

DEV&OPS/Java

NIO 파일 채널

ALEPH.GEM 2022. 5. 25. 11:20

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