我想从客户端(通过网络)读取 blobfield(使用 blobstream),但应用程序在获取数据时冻结。如何在不冻结的情况下读取 blob 字段并使用进度条显示百分比。(我正在使用德尔福和火鸟)
我正在使用唯一性组件。 我从以下位置找到了这段代码: 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:我已经设置了独特性的选项:
cacheblobs:= false;
streamedblobls:= true;
deferredblobread:= true;
在重复直到循环的第一步中,Blob.Read 方法读取所有流,因此它无法正常工作。
你应该使用一个线程,下面是一个 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
将进度条位置设置为在主线程中FPosition
。
不够大,这可能仍会阻止 UI(由于同步过度),因此请选择适当的块大小或添加一些更新延迟。
您必须确保在匿名线程运行时,UniQuery1
对象的连接不在主线程中使用,或者将连接和查询移动到线程。
这也可能存在重新输入问题,但它应该让您对如何使用线程进行后台处理有一个基本的了解。
PS:在线程中运行查询也可能是一个好主意,特别是如果可能需要一些时间。