C# 에서 파일을 저수준으로 직접 복사하는 방법, 파일을 복사할 때 멈추지 않도록 하기 - Whitmem
C# 에서 파일을 저수준으로 직접 복사하는 방법, 파일을 복사할 때 멈추지 않도록 하기
C# Language
2023-04-25 05:46 게시 b5a2d02fb85707480e4b

0
0
291
이 페이지는 외부 공간에 무단 복제할 수 없으며 오직 있는 그대로 게시되며 부정확한 내용을 포함할 수 있습니다. 법률이 허용하는 한 가이드 라인에 맞춰 게시 내용을 인용하거나 출처로 표기할 수 있습니다.
This page is not to be distributed to external services; it is provided as is and may contain inaccuracies.
파일 복사
C# 에서는 파일 복사를 위한 간단한 메소드들을 기본적으로 제공하고 있습니다.
하지만 이러한 메소드를 사용하게 되면 대용량의 파일을 복사하거나 처리하는 동안 쓰레드가 멈추며 이를 UI 프로세스에서 동작하는 경우 다른 작업을 같이 할 수 없는 문제가 발생합니다. 즉, 이 작업을 진행하느라 다른 작업은 멈춤 현상이 발생합니다. 보통 이러한 문제를 해결하기 위해서는 쓰레드를 사용하는 방법을 통해 작업을 동시에 실행할 수 있습니다.
위 소스코드를 활용하게 되면 copy 요청을 보냈을 때 쓰레드가 새로 시작되어 파일의 복사가 다른 쓰레드에서 처리됩니다. 파일의 용량이 매우 커 시간이 오래 걸린다고 하더라도 별개의 쓰레드에서 실행되기 때문에 thread.Start() 다음의 명령어인 MessageBox.Show("OK");는 바로 실행됩니다. 따라서 보통 UI에서 대용량의 파일을 복사하거나 멈춤 현상이 발생할 예정인 공간에서 쓰레드를 사용해야 합니다.
피드백 불가
상기 방법을 이용하면 메인 쓰레드를 멈추지 않고 작업을 같이할 수 있게 되지만, 피드백이 불가능하다는 문제점이 존재합니다. C#에서 기본적으로 제공하는 File.Copy 함수는 단순히 파일을 복사만 해주는 메소드로 구성되어 있습니다. 따라서 이 작업이 얼마나 완료되었는지 등 피드백을 받아 볼 방법이 없습니다. 그렇기 때문에 직접 파일 복사를 구현함으로써 어느정도 파일이 복사되었는지 나타내야 합니다.
상기 사진을 보면, copy 요청을 보내면 제 2의 쓰레드에서 파일 복사가 시작됩니다. 파일 복사가 완료되면 finish 을 true로 받아 시간이 지나 완료 여부를 확인할 수 있겠으나, 실시간 정도는 확인할 수 없습니다.
파일 복사 함수 구현
기본적으로 파일 복사를 직접 구현하고, 필요에 따라 퍼센트나 정도의 피드백을 받아볼 수 있겠습니다.
파일 스트림 열기
파일 복사를 위해서는 먼저 C#에서 제공중인 클래스인 "FileStream"을 사용 해 파일의 스트림을 열 수 있습니다. 사실 저 수준은 아니고, 일반적으로 제공하는 File IO 입니다.
기본적으로 FileStream을 통해 src의 주소 그리고 dst 주소의 스트림을 각각 만들어줍니다. 이 파일을 읽거나 쓸 수 있는 통로를 열어주는 작업이라고 볼 수 있습니다. 특히 두 번째 인자의 FileMode 는 파일을 단순히 열기만 할지, OpenOrCreate 로 생성도 할지 정합니다. 상기 화면과 같이 구현을 하면, 기본적으로 파일을 복사하는 대상인 dst 파일은 존재하지 않으므로 오류가 발생할 수 있습니다. 따라서 파일이 존재하지 않는 경우 dst 파일을 생성 해 주는 작업이 필요 합니다.
대상 파일이 존재하지 않는 경우 파일을 생성 해 주고, 복사하려는 대상에 파일이 이미 존재하는 경우 해당 파일을 제거하고 다시 만들 수 있도록 합니다. 우리가 하려는 작업은 복사작업이기 때문에, 붙여 넣고자 하는 위치에 이미 다른 파일이 존재하면 기존 위치에 추가해서는 안되기 때문입니다.
파일 스트림 읽기 및 쓰기
파일의 스트림을 열었다면 이제 스트림으로부터 데이터를 읽고 붙여넣기할 스트림에 써야 합니다.
여기서 신경 쓸 점은 파일을 한 번에 복사하지는 않는다는 것 입니다. 우리가 파일 복사 메소드를 사용하지 않는 이유는 중간 중간 정도의 피드백을 받아보기 위함이기 때문에, 파일을 조각 내어 복사해야 합니다. 조각 내어 복사하는 동안 조각의 길이에 따라 이를 진행 정도로 나타낼 수 있습니다. 즉 이를 "버퍼"로 볼 수 있습니다. 파일을 일정 크기로 조각 내면 파일의 끝에 도달할 때 까지 계속 파일의 조각을 새로운 대상에 붙여넣기 해 줘야 하기 때문에 이 부분은 WHILE문을 사용하여 무한 반복 해 줍니다. WHILE문 안에서는 한 조각을 가져오고 가져온 조각을 대상 스트림에 붙여넣는 작업을 진행합니다. 위 사진은 버퍼의 크기를 1024로 두어, 파일을 매 반복마다 1024 바이트씩 데이터를 읽어옵니다. 읽어진 데이터는 새로운 스트림에 WRITE 됩니다. 하지만 여기서 유의 할 점은 버퍼의 크기가 항상 일정하지는 않습니다. 파일의 크기가 1024BYTES 로 떨어지면 문제가 발생하지 않지만, 마지막 조각을 읽을 때 끝 일부분은 항상 1024 BYTES 라는 보장이 없기 때문입니다. 따라서 Read 작업을 통해 "읽어진 길이"를 확인 하고 그 읽어진 길이에 따라서 작업을 진행해야 합니다. 이 부분이 바로 다음과 같은 라인입니다.
int readedSize = sourceFileStream.Read(buffer, 0, buffer.Length);
이 코드에서 readedSize(readSize) 는 파일 스트림으로 부터 받아온 크기를 반환합니다. 따라서 1024일 수도 있고, 이보다 작을 수도 있습니다. 이렇게 읽어진 값을 대상 파일 스트림에 써줍니다. 단, 받아온 사이즈가 0보다 작은 경우 파일의 끝 지점에 도착한 것이기 때문에, break를 통해 작업을 중지합니다.
참고 읽어진 사이즈는 문법 상 readSize가 맞지만 (read-read 과거) 습관으로 readedSize라고 하는 점 양해 바랍니다.
그리고 모든 작업이 완료되면 Close()를 통해 flush 처리 및 스트림을 마무리 짓습니다.
진행 퍼센트 가져오기
파일 복사는 모두 구현했기 때문에 이제 이 중간에 퍼센트를 가져오면 됩니다. 퍼센트는 조각을 기준으로 가져오기 때문에 조각이 매우크면, 한 조각을 읽어오는 데 시간이 걸릴 수 있습니다. 따라서 보통 102400~512000 사이즈를 권장합니다.
먼저, while이 시작되기 전
long maxSize = sourceFileStream.Length; long totalWroteSize = 0;
로 초기 값을 지정 해 줍니다. maxSize는 복사하고자 하는 파일의 전체 길이를 가져오고, totalWroteSize 는 한 조각이 써질 때 마다 증가될 조각 크기입니다.
다음으로 while 내에는 다음과 같은 소스코드를 작성 해 넣습니다.
totalWroteSize += readedSize; Console.WriteLine(totalWroteSize + "/" + maxSize);
결과적으로 maxSize 변수에는 복사하고자 하는 파일의 전체 길이가, totalWroteSize에는 현재 작업까지 써진 파일의 총 길이가 됩니다. 이를 임시적으로 Console.WriteLine을 통해 콘솔창에 출력 해 봅니다. 이러한 작업을 통해 퍼센트로 구할 수 있습니다.
double percent = ((double)totalWroteSize / (double)maxSize)*100;
이렇게 구한 값은 ProgressBar 등에 적용하여 사용할 수 있습니다. 여기서 주의 할 점은 각 양 변수를 double로 처리하여 처리 결과도 double로 나올 수 있도록 합니다. 파일의 용량은 4GB를 넘을 수 있기 때문에, long으로 선언되어 있는데, long으로 나눗셈을 하는 경우 소수점은 모두 제거되기 때문에 100% 일 때만 1이 되고 나머지의 경우 모두 0으로 처리되는 문제가 발생할 수 있습니다.
댓글 0개
댓글은 일회용 패스워드가 발급되며 사이트 이용 약관에 동의로 간주됩니다.
확인
Whitmemit 개인 일지 블로그는 개인이 운영하는 정보 공유 공간으로 사용자의 민감한 개인 정보를 직접 요구하거나 요청하지 않습니다. 기본적인 사이트 방문시 처리되는 처리 정보에 대해서는 '사이트 처리 방침'을 참고하십시오. 추가적인 기능의 제공을 위하여 쿠키 정보를 사용하고 있습니다. Whitmemit 에서 처리하는 정보는 식별 용도로 사용되며 기타 글꼴 및 폰트 라이브러리에서 쿠키 정보를 사용할 수 있습니다.
이 자료는 모두 필수 자료로 간주되며, 사이트 이용을 하거나, 탐색하는 경우 동의로 간주합니다.