我想从客户端(通过网络)读取blobfield(使用blobstream),但应用程序在获取数据时会冻结。如何在不冻结的情况下阅读blobfield并使用进度条显示百分比。 (我正在使用Delphi和Firebird)
我正在使用uniquery组件。我发现了以下代码:http://forums.devart.com/viewtopic.php?t=14629
但它无法正常工作:
const
BlockSize= $F000;
var
Blob: TBlob;
Buffer: array of byte;
p: pointer;
pos, count: integer;
UniQuery1.SQL.Text:= 'select * from TABLE1 where FIELD_ID = 1';
UniQuery1.Open;
blob:= uniquery1.GetBlob('DATA');
SetLength(buffer, blob.Size);
ProgressBar1.Position:= 0;
Application.ProcessMessages;
repeat
count:= Blob.Read(pos, blocksize, p);
ProgressBar1.Position:= Round(pos/Blob.Size * 100);
pos:= pos + count;
p:= pointer(integer(p) + count);
Application.ProcessMessages;
until count < blocksize;
PS:我设置了uniquery的选项:
cacheblobs:= false;
streamedblobls:= true;
deferredblobread:= true;
在repeat-until循环的第一步中,Blob.Read方法读取所有流,因此它无法正常工作。
答案 0 :(得分:1)
你应该使用一个线程,这是Delphi TThread
的一个例子:
type
TMyForm = class(TForm)
private
FPosition: Integer;
procedure ProgressUpdate;
procedure Execute;
end;
procedure TMyForm.ProgressUpdate;
begin
ProgressBar1.Position := FPosition;
end;
procedure TMyForm.Execute;
begin
FPosition:= 0;
ProgressUpdate;
Thread := TThread.CreateAnonymousThread(procedure
begin
repeat
// Do some long running stuff (in chunks, so we can update the position)
FPosition := CalculatePosition;
// Important: Synchronize will run ProgressUpdate in the main thread!
TThread.Synchronize(nil, ProgressUpdate);
until SomeCondition;
end
);
Thread.Start;
end;
因此,在将此模式应用于您的代码后,我们得到:
type
TMyForm = class(TForm)
private
FPosition: Integer;
procedure ProgressUpdate;
procedure Execute;
end;
procedure TMyForm.ProgressUpdate;
begin
ProgressBar1.Position := FPosition;
end;
procedure TMyForm.Execute;
var
Blob: TBlob;
Thread: TThread;
begin
UniQuery1.SQL.Text := 'SELECT * FROM TABLE1 WHERE FIELD_ID = 1';
UniQuery1.Open;
Blob := UniQuery1.GetBlob('DATA');
FPosition:= 0;
ProgressUpdate;
Thread := TThread.CreateAnonymousThread(
procedure
const
BlockSize = $F000;
var
Buffer: array of Byte;
P: Pointer;
Pos, Count: Integer;
begin
SetLength(Buffer, Blob.Size);
repeat
Count := Blob.Read(Pos, BlockSize, P);
FPosition := Round(Pos / Blob.Size * 100);
Pos := Pos + Count;
P := Pointer(Integer(P) + Count);
// Important: Synchronize will run ProgressUpdate in the main thread!
TThread.Synchronize(nil, ProgressUpdate);
until Count < BlockSize;
end
);
Thread.Start;
end;
我删除了Application.ProcessMessage
并将所有处理移到了帖子中。
线程正在设置FPosition
私有属性,并使用TThread.Synchronize
在主线程中将ProgressBar位置设置为FPosition
。
如果您的块大小不够大,这可能仍会阻止UI(由于过度同步),因此请选择适当的块大小或添加一些更新延迟。
您必须确保在匿名线程运行时主线程中未使用UniQuery1
对象的连接,或者也将连接和查询移动到线程中。
此外,这可能会出现重入问题,但它应该让您了解如何使用线程进行后台处理。
PS:在线程中运行查询也是一个好主意,特别是如果它可能需要一些时间。