ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] NIO 기반 입출력 및 네트워킹 - 파일 채널
    CSE/Java 2015. 9. 3. 14:43
    NIO 기반 입출력 및 네트워킹은 여러 절로 구성되어 있습니다.




    4. 파일 채널(File Channel)

     java.nio.channels.FileChannel을 이용하면 파일 읽기와 쓰기를 할 수 있습니다.


     FileChannel은 동기화 처리가 되어 있기 때문에 멀티 스레드 환경에서 사용해도 안전합니다.








     4.1 FileChannel 생성과 닫기

      FileChannel 은 정적 메소드인 open()을 호출해서 얻을 수도 있지만, IO의 FileInputStream, FileOutputStream의 getChannel() 메소드를 호출해서도 얻을 수 있습니다.


      다음은 open() 메소드로 FileChannel을 얻는 방법을 보여줍니다.



    1
    2
    FileChannel fileChannel = FileChannel.open(Path path, OpenOption... options);
     
    cs




      첫번째 path 파라미터는 열거나, 생성하고자 하는 파일의 경로를 Path 객체로 생성해서 지정하면 되고, 두번째 options 파라미터는 열기 옵션 값인데 StandardOpenOption의 다음 열거 상수를 나열해주면 됩니다.






      


      예를 들어 "C:\r_temp\file.txt"를 생성하고, 어떤 내용을 쓰고 싶다면 다음과 같이 파라미터를 지정하면 됩니다.



    1
    2
    3
    4
    5
    6
    FileChannel fileChannel = FileChannel.open(
        Paths.get("c:/r_temp/file.txt"), 
        StandardOpenOtion.CREATE_NEW, 
        StandardOpenOption.WRITE
    );
     
    cs




      다음은 "C:\r_temp\file.txt"를 읽고 쓸 수 있도록 채널을 생성합니다.


    1
    2
    3
    4
    5
    6
    FileChannel fileChannel = FileChannel.open(
        Paths.get("c:/r_temp/file.txt"), 
        StandardOpenOtion.READ, 
        StandardOpenOption.WRITE
    );
     
    cs




      FileChannel을 더 이상 사용하지 않을 경우에는 다음과 같이 close() 메소드를 호출해서 닫아주면 됩니다.


    1
    2
    fileChannel.close();
     
    cs








     4.2 파일 쓰기와 읽기

      파일에 바이트를 쓰려면 다음과 같이 FileChannel()의 write() 메소드를 호출하면 됩니다.


      파라미터로 ByteBuffer 객체를 주면 되는데, 파일에 쓰여지는 바이트는 ByteBuffer의 position부터 limit까지 입니다.


      position이 0이고 limit이 capacity와 동일하다면 ByteBuffer의 모든 바이트가 파일에 쓰여집니다.


      write() 메소드의 리턴 값은 ByteBuffer에서 파일로 쓰여진 바이트 수입니다.


    1
    2
    int byteCount = fileChannel.write(ByteBuffer src);
     
    cs




      아래 예제는 FileChannel을 이용해서 문자열을 c:\r_temp\file.txt 파일에 저장하는 예제입니다.



     * FileChannelWriteExample.java



    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
     
    package pathexam;
     
    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 FileChannelWriteExample {
        public static void main(String[] args) throws Exception {
            Path path = Paths.get("c:/r_temp/file.txt");
            Files.createDirectories(path.getParent());
     
            FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
     
            String data = "Hello !!";
            Charset charset = Charset.defaultCharset();
            ByteBuffer byteBuffer = charset.encode(data);
     
            int byteCount = fileChannel.write(byteBuffer);
            System.out.println("file.txt: " + byteCount + " bytes written");
     
        }
     
    }
     
    cs












      이번에는 파일 읽기를 살펴보도록 하겠습니다.


      파일로부터 바이트를 읽기 위해서는 다음과 같이 FileChannel의 read() 메소드를 호출하면 됩니다.


      파라미터로 ByteBuffer 객체를 주면 되는데, 파일에서 읽혀지는 바이트는 ByteBuffer의 position부터 저장됩니다.


      position이 0이면 ByteBuffer의 첫 바이트부터 저장됩니다.


      read() 메소드의 리턴 값은 파일에서 ByteBuffer로 읽혀진 바이트 수입니다.


      한 번 읽을 수 있는 최대 바이트 수는 ByteBuffer의 capacity까지이므로 리턴되는 최대 값은 capacity가 됩니다.


      더 이상 읽을 바이트가 없다면 read() 메소드는 -1을 리턴합니다.


     

    1
    2
    int bytesCount = fileChannel.read(ByteBuffer dst);
     
    cs




      버퍼에 한 바이트를 저장할 때마다 position이 1씩 증가하게 되는데, read() 메소드가 -1을 리턴할 때까지 버퍼에 저장한 마지막 바이트의 위치는 position -1 인덱스입니다. 다음 예제는 이전 예제에서 생성한 c:\r_temp\file.txt 파일을 읽고 콘솔에 출력합니다.



     * FileChannelReadExample.java


    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
     
    package pathexam;
     
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.charset.Charset;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.nio.file.StandardOpenOption;
     
    public class FileChannelReadExample {
     
        public static void main(String[] args) throws IOException {
            Path path = Paths.get("c:/r_temp/file.txt");
     
            FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ);
     
            ByteBuffer buffer = ByteBuffer.allocate(100);
     
            Charset charset = Charset.defaultCharset();
            String data = "";
     
            int byteCount;
     
            while (true) {
                byteCount = fileChannel.read(buffer);
     
                if (byteCount == -1)
                    break;
     
                buffer.flip();
                data += charset.decode(buffer).toString();
                buffer.clear();
            }
     
            fileChannel.close();
     
            System.out.println("data : " + data);
        }
     
    }
     
    cs









      파일의 크기가 100바이트 보다 작지만 예제에서는 ByteBuffer의 크기를 100으로 주었습니다. 예제에서 FileChannel의 read() 메소드를 호출해서 최대 100바이트를 읽습니다. 그러나 파일의 크기가 100바이트보다 작으므로 byteCount에는 100보다 작은 값이 저장됩니다. 반복문에서 flip()을 호출한 이유는 limit을 현재 position으로 설정하고 position을 0으로 설정하기 위해서 입니다. position에서 limit까지 읽고 문자열로 변환합니다. clear() 호출 이유는 position을 0번 인덱스로, limit을 capacity로 설정해서 ByteBuffer를 초기화하기 위해서 입니다.





     4.3 파일 복사

      파일 복사를 구현하기 위해서는 하나의 ByteBuffer를 사이에 두고, 파일 읽기용 FileChannel과 파일 쓰기용 FileChannel이 읽기와 쓰기를 교대로 번갈아 수행하도록 하면 됩니다.


      






      다음 예제는 FileChannel을 이용해서 이미지 파일을 복사합니다. 


      이 예제에서는 크기가 100인 다이렉트 버퍼를 생성하였습니다. 채널에서 읽고 다시 채널로 쓰는 경우 다이렉트 버퍼가 좋은 성능을 내기 때문입니다.










      


      * FileCopyExample.java


    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
     
    package pathexam;
     
    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 FileCopyExam {
     
        public static void main(String[] args) throws IOException {
            Path from = Paths.get("c:/r_temp/file.txt");
            Path to = Paths.get("c:/r_temp/file_copy.txt");
     
            FileChannel fileChannel_from = FileChannel.open(from, StandardOpenOption.READ);
            FileChannel fileChannel_to = FileChannel.open(from, StandardOpenOption.WRITE);
     
            ByteBuffer buffer = ByteBuffer.allocateDirect(100);
     
            int byteCount = 0;
     
            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("파일 복사 성공!!");
        }
     
    }
     
    cs











      이번 예제처럼 ByteBuffer와 FileChannel 2개를 직접 생성해서 복사를 구현해도 좋지만, 단순히 파일을 복사할 목적이라면 NIO의 Files 클래스의 copy() 메소드를 사용하는 것이 더 편리합니다.


    1
    2
    Path targetPath = Files.copy(Path source, Path target, CopyOption ...options);
     
    cs



      첫 번째 파라미터 source는 원본 파일의 Path 객체를 지정하고, 두 번째 파라미터 target은 타겟 파일의 Path 객체를 지정해주면 됩니다. 세 번째 파라미터는 다음 세 가지 StandardCopyOption 열거 상수를 목적에 맞게 나열해주면 됩니다.





      열거 상수

      설명 

     REPLACE_EXISTING

     타겟 파일이 존재하면 대체한다. 

     COPY_ATTRIBUTES 

     파일의 속성까지도 복사한다. 

     NOFOLLOW_LINKS 

     링크 파일일 경우 링크 파일만 복사하고 링크된 파일은 복사하지 않는다. 




      다음 예제는 Files 클래스의 copy() 메소드를 이용해서 이미지 파일을 복사합니다.


      * FilesCopyMethodExam.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    package pathexam;
     
    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 FilesCopyMethodExam {
     
        public static void main(String[] args) throws IOException {
            Path from = Paths.get("c:/r_temp/file_copy.txt");
            Path to = Paths.get("c:/r_temp/file_copy2.txt");
     
            Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
            System.out.println("파일 복사 성공!");
        }
     
    }
     
    cs












    * 이 포스트은 서적 '이것이 자바다' 를 참고하여 작성한 포스트입니다.

    댓글

Designed by Tistory.