我想在TFileStream
上实现一个进度事件,用于读/写操作,为其分配一个进度条。
我创建了一个TProgressFileStream
TFileStream
)
unit ProgressFileStream;
interface
uses
System.SysUtils,
System.Classes;
type
TProgressFileStreamOnProgress = procedure(Sender: TObject; Processed: Int64; Size: Int64; ContentLength : Int64; TimeStart : cardinal) of object;
TProgressFileStream = class(TFileStream)
private
FOnProgress: TProgressFileStreamOnProgress;
FProcessed: Int64;
FContentLength: Int64;
FTimeStart: cardinal;
FBytesDiff: cardinal;
FSize: Int64;
procedure Init;
procedure DoProgress(const AProcessed : Longint);
protected
procedure SetSize(NewSize: Longint); overload; override;
public
constructor Create(const AFileName: string; Mode: Word); overload;
constructor Create(const AFileName: string; Mode: Word; Rights: Cardinal); overload;
function Read(var Buffer; Count: Longint): Longint; overload; override;
function Write(const Buffer; Count: Longint): Longint; overload; override;
function Read(Buffer: TBytes; Offset, Count: Longint): Longint; overload; override;
function Write(const Buffer: TBytes; Offset, Count: Longint): Longint; overload; override;
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
property OnProgress: TProgressFileStreamOnProgress read FOnProgress write FOnProgress;
property ContentLength: Int64 read FContentLength write FContentLength;
property TimeStart: cardinal read FTimeStart write FTimeStart;
property BytesDiff: cardinal read FBytesDiff write FBytesDiff;
end;
implementation
uses
Winapi.Windows;
{ TProgressFileStream }
constructor TProgressFileStream.Create(const AFileName: string; Mode: Word);
begin
inherited Create(AFileName, Mode);
Init;
end;
constructor TProgressFileStream.Create(const AFileName: string; Mode: Word; Rights: Cardinal);
begin
inherited Create(AFileName, Mode, Rights);
Init;
end;
function TProgressFileStream.Read(var Buffer; Count: Longint): Longint;
begin
Result := inherited Read(Buffer, Count);
DoProgress(Result);
end;
function TProgressFileStream.Write(const Buffer; Count: Longint): Longint;
begin
Result := inherited Write(Buffer, Count);
DoProgress(Result);
end;
function TProgressFileStream.Read(Buffer: TBytes; Offset, Count: Longint): Longint;
begin
Result := inherited Read(Buffer, Offset, Count);
DoProgress(Result);
end;
function TProgressFileStream.Write(const Buffer: TBytes; Offset, Count: Longint): Longint;
begin
Result := inherited Write(Buffer, Offset, Count);
DoProgress(Result);
end;
function TProgressFileStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
begin
Result := inherited Seek(Offset, Origin);
if Origin <> soCurrent then
FProcessed := Result;
end;
procedure TProgressFileStream.SetSize(NewSize: Longint);
begin
inherited SetSize(NewSize);
FSize := NewSize;
end;
procedure TProgressFileStream.Init;
const
BYTES_DIFF = 1024*100;
begin
FOnProgress := nil;
FProcessed := 0;
FContentLength := 0;
FTimeStart := GetTickCount;
FBytesDiff := BYTES_DIFF;
FSize := Size;
end;
procedure TProgressFileStream.DoProgress(const AProcessed : Longint);
var
aCurrentProcessed : Longint;
begin
if not(Assigned(FOnProgress)) then Exit;
aCurrentProcessed := FProcessed;
Inc(FProcessed, AProcessed);
if FContentLength = 0 then
FContentLength := FSize;
if (FProcessed = FSize) or (FBytesDiff = 0) or (aCurrentProcessed - FBytesDiff < FProcessed) then
FOnProgress(Self, FProcessed, FSize, FContentLength, FTimeStart);
end;
end.
基本用法是
procedure TWinMain.ProgressFileStreamOnProgressUpload(Sender: TObject; Processed: Int64; Size: Int64; ContentLength : Int64; TimeStart : cardinal);
begin
if Processed > 0 then
ProgressBar.Position := Ceil((Processed/ContentLength)*100);
end;
procedure TWinMain.BtnTestClick(Sender: TObject);
const
ChunkSize = $F000;
var
aBytes: TBytes;
aBytesRead : integer;
aProgressFileStream : TProgressFileStream;
begin
aProgressFileStream := TProgressFileStream.Create('MyFile.zip', fmOpenRead or fmShareDenyWrite);
SetLength(aBytes, ChunkSize);
try
aProgressFileStream.OnProgress := ProgressFileStreamOnProgressUpload;
aProgressFileStream.Seek(0, soFromBeginning);
repeat
aBytesRead := aProgressFileStream.Read(aBytes, ChunkSize);
until (aBytesRead = 0);
finally
aProgressFileStream.Free;
end;
end;
问题是在方法中调用事件,我想调用每个FBytesDiff
的事件(默认情况下每个100 KB):
procedure TProgressFileStream.DoProgress(const AProcessed : Longint);
var
aCurrentProcessed : Longint;
begin
if not(Assigned(FOnProgress)) then Exit;
aCurrentProcessed := FProcessed;
Inc(FProcessed, AProcessed);
if FContentLength = 0 then
FContentLength := Size;
if (FProcessed = Size) or (FBytesDiff = 0) or (FProcessed - aCurrentProcessed > FBytesDiff) then
FOnProgress(Self, FProcessed, Size, FContentLength, FTimeStart);
end;
但事件似乎在每个ChunkSize(61440字节 - 60 KB)...
上触发我想添加此控件,以免因过多事件调用而浪费流读/写性能。
答案 0 :(得分:5)
FProcessed - aCurrentProcessed将返回Chunk Size。我认为你应该创建一个变量来存储读取块FReadSize
,用0初始化它。如果读取的大小大于FBytesDiff从FReadSize中减去FBytesDiff,则增加该变量和读取的字节。
procedure TProgressFileStream.DoProgress(const AProcessed : Longint);
var
aCurrentProcessed : Longint;
begin
if not(Assigned(FOnProgress)) then Exit;
aCurrentProcessed := FProcessed;
Inc(FProcessed, AProcessed);
Inc(FReadSize, AProcessed);
if FContentLength = 0 then
FContentLength := Size;
if (FProcessed = Size) or (FBytesDiff = 0) or (FReadSize >= FBytesDiff) then
begin
FOnProgress(Self, FProcessed, Size, FContentLength, FTimeStart);
FReadSize := FReadSize - FBytesDiff;
end;
end;