Einzelnen Beitrag anzeigen

Peter666

Registriert seit: 11. Aug 2007
357 Beiträge
 
#1

Downloadlogik unter Firemonkey

  Alt 20. Feb 2020, 17:15
Hallo,

ich habe mal eine Frage wie ihr folgendes Problem lösen würdet. Bei einem Projekt lade ich diverse Grafiken von einem Server zeige diese in einer Grid an. Damit das UI keinen Herzinfarkt bekommt, lade ich die Bilder im Hintergrund. Mein Code ist folgender:

Delphi-Quellcode:
function GetHtml(const AUri: String; Stream: TStream): Boolean;
var
{$IFDEF USEINDY}
  http: TidHttp;
{$ELSE}
  http: THttpClient;
{$ENDIF}
begin
  Result := false;
  http := {$IFDEF USEINDY} TidHttp.Create(nil) {$ELSE} THttpClient.Create
{$ENDIF};
  try
    try
      http.Get(AUri, Stream);
      Result := true;
      Stream.Position := 0;
    except
    end;
  finally
    http.DisPoseOf;
  end;
end;

procedure RetrieveImage(ALogo: String; const AZip: TZipFile;
  const AOnAvailable: TOnStreamAvailable; const ACached: Boolean);
begin
  if assigned(AOnAvailable) then
    RetrieveImage(ALogo, AZip,
      procedure(const AStream: TStream)
      begin
        AOnAvailable(AStream);
      end, ACached);
end;

procedure RetrieveImage(ALogo: String; const AZip: TZipFile;
const AOnAvailable: TOnStreamAvailableDirect; const ACached: Boolean);
var
  Stream: TStream;
  LocalHeader: TZipHeader;

begin
  if not assigned(AOnAvailable) then
    exit;
  if (Pos('://', ALogo) = 0) and FileExists(ALogo) then
  begin
    Stream := TMemoryStream.Create;
    try
      TMemoryStream(Stream).LoadFromFile(ALogo);
    except
    end;
    AOnAvailable(Stream);
    Stream.DisPoseOf;
  end
  else if assigned(AZip) and (Pos('zip://', ALogo) > 0) then
  begin
    ALogo := StringReplace(ALogo, 'zip://', '', [rfReplaceAll]);
    if AZip.IndexOf(ALogo) > -1 then
    begin
      FLock.Enter;
      AZip.Read(ALogo, Stream, LocalHeader);
      FLock.Leave;
      AOnAvailable(Stream);
      Stream.DisPoseOf;
    end;
  end
  else if (Pos('http://', ALogo) > 0) or (Pos('https://', ALogo) > 0) then
  begin
    TTask.Run(
      procedure()
      var
        Stream: TMemoryStream;
        Filename: String;
      begin
        Filename := TPath.Combine(TPath.GetTempPath, IntToStr(Crc32(ALogo)) +
          ExtractFileExt(ALogo));
        Stream := TMemoryStream.Create;
        if (ACached) and FileExists(Filename) then
          try
            Stream.LoadFromFile(Filename)
          except
          end
        else if GetHtml(ALogo, Stream) and ACached then
          try
            Stream.SaveToFile(Filename);
          except
          end;
        Stream.Position := 0;

        TThread.Queue(nil,
          procedure()
          begin
            if assigned(AOnAvailable) then
              AOnAvailable(Stream);
            Stream.Free;
          end);
      end).Start;
  end;
end;
Ich bin nicht richtig glücklich damit, da unter Umständen ein Bild doppelt geladen werden kann und ich im schlimmsten Fall das Bild speichere und gleichzeitig in einem anderen Thread schon lade. Man müsste das jetzt absichern. Mein Ansatz wäre eine CriticalSection beim speichern und laden zu verwenden und in einem Dictionary Url und Dateiname zu hinterlegen. Kennt jemand eventuell eine elegantere Variante?

Das zweite Problem sind defekte Downloads. Ich kriege die ja nicht mit und ab und wenn man jetzt ein kaputtes Bild einem TBitmap.CreateFromStream oder LoadFromStream übergibt, gibts eine Exception. Nach einiger Zeit hilft aber kein try except end und die Anwendung wird klaglos beendet. Zumindest auf Android. Schaut man dann in die Konsole steht meist irgendwas mit OpenGL fatal irgendwas als Grund für das Schließen der App. Ich hab absolut keine Ahnung inwieweit ich die Validität der geladenen Bilder überprüfe und bei offensichtlichem Unsinn ignoriere.

Das dritte Problem ist wie und wann lösche ich den temporären Pfad? Muss ich dass überhaupt, oder säubert das Android bzw. IOS von alleine?

Peter
  Mit Zitat antworten Zitat