Delphi, C++Builder 개발자들에게 TStream 클래스는 아주 친숙하죠. TStream은 TFileStream, TMemoryStream, TBlobStream, TWinSocketStream 등으로 상속되어 TStream의 인터페이스로 여러 컴포넌트 및 루틴들에서 사용됩니다. 특히 TFileStream은 Delphi와 C++Builder에서 로컬의 파일을 읽어들이고 쓰는 데 가장 일반적인 방법으로 많이 쓰이고 있습니다. 그런데, 대용량의 파일을 읽고 쓰는 작업을 하거나 혹은 네트워크를 통해 상당히 큰 파일을 주고받는다면 지연되는 시간 동안 ProgressBar 등으로 현재 진행 상황을 보여주고 싶을 수 있습니다.
첨부한 TImpFileStream 클래스는 이 TFileStream 클래스에 Size와 Position이 바뀔 때마다 이벤트가 발생하도록 한 간단한 클래스로, 이런 목적에 사용할 수 있습니다. TFileStream을 상속한 클래스이므로 TFileStream이 사용되는 모든 경우에 사용할 수 있습니다. 스트림 클래스에 Size 속성의 값이 바뀔 때마다 OnSizeChange 이벤트가 발생하며, Position이 변경되면 OnPositionChange 이벤트가 발생합니다.
이 클래스는 특히 파일을 네트워크로 보내거나 반대로 네트워크로 받은 데이터를 파일로 저장할 때 유용합니다. Delphi/C++Builder에 포함된 TidFTP와 TidHTTP 등은 OnWork 이벤트를 가지고 있어 이런 목적에 사용할 수 있지만, Indy 외에 다른 서드파티 네트워크 컴포넌트 라이브러리인 ICS(THttpCli, TFtpClient 등)처럼 진행상황을 표시할 이벤트가 없는 경우도 있습니다. Indy 라이브러리의 경우에도 일부 버전에서는 OnWork 이벤트의 구현에 버그가 있어 제대로 동작하지 않는 경우가 있기 때문에 이 방식이 유용할 수 있습니다. 또한 TResourceStream이나 TBLOBStream, TWinSocketStream 등의 다른 스트림 종류에 대해서도 이런 방식을 응용할 수 있습니다.
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 |
unit ImpFileStream; interface uses Classes; type TNumChangeEvent = procedure (Sender: TObject; OldPos, NewPos: integer) of object; TImpFileStream = class(TFileStream) private FOldSize: integer; FOldPos: integer; FOnSizeChange: TNumChangeEvent; FOnPositionChange: TNumChangeEvent; procedure GetSizePos; procedure CheckSizePos; protected procedure SetSize(NewSize: integer); override; public function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; function Seek(Offset: Longint; Origin: Word): Longint; override; property OnSizeChange: TNumChangeEvent read FOnSizeChange write FOnSizeChange; property OnPositionChange: TNumChangeEvent read FOnPositionChange write FOnPositionChange; end; implementation uses SysUtils; procedure TImpFileStream.GetSizePos; begin if (not Assigned(FOnSizeChange)) and (not Assigned(FOnPositionChange)) then exit; FOldPos := Position; FOldSize := Size; end; procedure TImpFileStream.CheckSizePos; begin if (not Assigned(FOnSizeChange)) and (not Assigned(FOnPositionChange)) then exit; if Assigned(FOnSizeChange) and (FOldSize<>Size) then FOnSizeChange(self, FOldSize, Size); if Assigned(FOnPositionChange) and (FOldPos<>Position) then FOnPositionChange(self, FOldPos, Position); end; procedure TImpFileStream.SetSize(NewSize: integer); begin GetSizePos; inherited SetSize(NewSize); CheckSizePos; end; function TImpFileStream.Read(var Buffer; Count: Longint): Longint; begin GetSizePos; Result := inherited Read(Buffer, Count); CheckSizePos; end; function TImpFileStream.Write(const Buffer; Count: Longint): Longint; begin GetSizePos; Result := inherited Write(Buffer, Count); CheckSizePos; end; function TImpFileStream.Seek(Offset: Longint; Origin: Word): Longint; begin GetSizePos; Result := inherited Seek(Offset, Origin); CheckSizePos; end; end. |
이 클래스를 사용하려면 OnSizeChange 혹은 OnPositionChange 이벤트의 핸들러를 직접 구현하고 TImpFileStream 객체의 이벤트에 연결해주면 됩니다. 예를 들면, 아래와 같이 스트림 객체를 생성하고 이벤트 핸들러에 연결해줍니다.
1 2 3 4 5 6 7 8 9 |
FileStream := TImpFileStream.Create([파일경로], fmOpenRead); try FileStream.OnPositionChange := FilePositionChange; ProgressBar1.Max := FileStream.Size; ProgressBar1.Position := 0; ... finally FileStream.Free; end; |
이벤트 핸들러는 다음과 같은 식이 될 것입니다.
1 2 3 4 5 |
procedure TForm1.FilePositionChange(Sender: TObject; OldPos, NewPos: integer); begin ProgressBar1.Position := NewPos; Application.ProcessMessages; end; |
오상현님께서 이 TImpFileStream 클래스를 파이어몽키 모바일에서 활용한 예를 글로 써 올리셨네요. 참고하시길.
http://cafe.naver.com/delphifmx/420
감사합니다. 잘 사용하겠습니다.