Как я могу читать blobfield без замораживания?

Я хочу прочитать blobfield (с blobstream) со стороны клиента (по сети), но приложение зависает при получении данных. Как я могу читать blobfield без зависания и показа процентов с помощью индикатора выполнения. (Я использую Delphi и Firebird)

Я использую уникальный компонент. я нашел этот код с: 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 считывает весь поток, поэтому он не работает должным образом.

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 в основной теме.

Если размер вашего блока недостаточно велик, это может блокировать пользовательский интерфейс (из-за чрезмерной синхронизации), поэтому выберите подходящий размер блока или добавьте некоторую задержку обновления.

Вы должны убедиться, что соединение UniQuery1 объект не используется в главном потоке, пока работает анонимный поток, или перемещает соединение, а также запрашивает поток.

Также могут возникнуть проблемы с повторным входом, но это должно дать вам базовое представление о том, как использовать поток для фоновой обработки.

PS: также может быть хорошей идеей запустить запрос в потоке, особенно если это может занять некоторое время.

Другие вопросы по тегам