Einzelnen Beitrag anzeigen

mytbo

Registriert seit: 8. Jan 2007
472 Beiträge
 
#18

AW: Memory Leak nach Zuweisung eines Resourcestream

  Alt 8. Jun 2022, 19:11
habe ich Dich richtig verstanden, Du bindest alle Bilder (png, jepeg ua) in eine Zip-Datei und holst sie dann bei Bedarf aus der Datei?
Als Beispiel habe ich dir eine Umsetzung mit mORMot geschrieben.

Disclaimer: Das Beispiel ist ein Proof of Concept, der Sourcecode ist weder getestet noch optimiert.
Delphi-Quellcode:
uses
  mormot.core.base,
  mormot.core.data,
  mormot.core.text,
  mormot.core.os,
  mormot.core.zip,
  mormot.core.perf,
  mormot.crypt.core;

type
  TImageResourceFile = class(TObject)
  private
    FPassword: RawUtf8;
    FFileName: TFileName;
    FIsWritable: Boolean;
  protected
    function LoadStream(pmStream: TCustomMemoryStream; const pmcResName: TFileName; const pmcPassword: RawUtf8): Boolean;
    procedure SaveStream(pmStream: TCustomMemoryStream; const pmcResName: TFileName; const pmcPassword: RawUtf8; pmIsCompressed: Boolean = True);
  public
    constructor Create(const pmcFileName: TFileName; const pmcPassword: RawUtf8);
    destructor Destroy; override;
    function LoadImage(pmImage: TImage; const pmcImageName: String): Boolean;
    procedure SaveImage(pmImage: TImage; const pmcImageName: String); overload;
    procedure SaveImage(pmImageData: TCustomMemoryStream; const pmcImageName: String); overload;
  end;

constructor TImageResourceFile.Create(const pmcFileName: TFileName; const pmcPassword: RawUtf8);
begin
  inherited Create;
  FFileName := pmcFileName;
  FPassword := pmcPassword;
  FIsWritable := IsDirectoryWritable(ExtractFilePath(FFileName));
end;

destructor TImageResourceFile.Destroy;
begin
  FillZero(FPassword);
  inherited Destroy;
end;

function TImageResourceFile.LoadStream(pmStream: TCustomMemoryStream; const pmcResName: TFileName; const pmcPassword: RawUtf8): Boolean;
var
  idx: Integer;
  zipRead: TZipRead;
  encStream: TMemoryStream;
  aesReader: TAesPkcs7Reader;
begin
  Result := False;
  if pmStream = Nil then Exit; //=>
  if pmcResName = 'then Exit; //=>
  if not FileExists(FFileName) then Exit; //=>

  zipRead := TZipRead.Create(FFileName);
  try
    idx := zipRead.NameToIndex(pmcResName);
    if idx < 0 then Exit; //=>

    if pmcPassword = 'then
      Result := zipRead.UnZip(idx, pmStream)
    else
    begin
      encStream := TMemoryStream.Create;
      try
        if zipRead.UnZip(idx, encStream) then
        begin
          encStream.Position := 0;
          aesReader := TAesPkcs7Reader.Create(encStream, pmcPassword);
          try
            Result := (StreamCopyUntilEnd(aesReader, pmStream) > 0);
          finally
            aesReader.Free;
          end;
        end;
      finally
        encStream.Free;
      end;
    end;
  finally
    zipRead.Free;
  end;
end;

procedure TImageResourceFile.SaveStream(pmStream: TCustomMemoryStream; const pmcResName: TFileName; const pmcPassword: RawUtf8; pmIsCompressed: Boolean);
const
  IS_COMPRESSED: array[Boolean] of Integer = (0, 6);
var
  zipWrite: TZipWrite;
  encStream: TMemoryStream;
  aesWriter: TAesPkcs7Writer;
begin
  if pmStream = Nil then Exit; //=>
  if pmcResName = 'then Exit; //=>
  if not FIsWritable then Exit; //=>

  if FileExists(FFileName) then
    zipWrite := TZipWrite.CreateFromIgnore(FFileName, [pmcResName])
  else
    zipWrite := TZipWrite.Create(FFileName);

  try
    if pmcPassword = 'then
      zipWrite.AddDeflated(pmcResName, pmStream.Memory, pmStream.Size, IS_COMPRESSED[pmIsCompressed], DateTimeToFileDate(Now))
    else
    begin
      encStream := TMemoryStream.Create;
      try
        aesWriter := TAesPkcs7Writer.Create(encStream, pmcPassword);
        try
          pmStream.Position := 0;
          StreamCopyUntilEnd(pmStream, aesWriter);
          aesWriter.Finish;
        finally
          aesWriter.Free;
        end;

        zipWrite.AddDeflated(pmcResName, encStream.Memory, encStream.Position, IS_COMPRESSED[pmIsCompressed], DateTimeToFileDate(Now));
      finally
        encStream.Free;
      end;
    end;
  finally
    zipWrite.Free;
  end;
end;

function TImageResourceFile.LoadImage(pmImage: TImage; const pmcImageName: String): Boolean;
var
  tmpStream: TMemoryStream;
begin
  Result := False;
  if pmImage = Nil then Exit; //=>
  if pmcImageName = 'then Exit; //=>

  tmpStream := TMemoryStream.Create;
  try
    if LoadStream(tmpStream, pmcImageName, FPassword) then
    begin
      tmpStream.Position := 0;
      try
        pmImage.Picture.LoadFromStream(tmpStream);
        Result := True;
      except
      end;
    end;
  finally
    tmpStream.Free;
  end;
end;

procedure TImageResourceFile.SaveImage(pmImage: TImage; const pmcImageName: String);
var
  tmpStream: TMemoryStream;
begin
  if pmImage = Nil then Exit; //=>
  if pmcImageName = 'then Exit; //=>

  tmpStream := TMemoryStream.Create;
  try
    pmImage.Picture.SaveToStream(tmpStream);
    SaveImage(tmpStream, pmcImageName);
  finally
    tmpStream.Free;
  end;
end;

procedure TImageResourceFile.SaveImage(pmImageData: TCustomMemoryStream; const pmcImageName: String);
begin
  if pmImageData = Nil then Exit; //=>
  if pmcImageName = 'then Exit; //=>

  SaveStream(pmImageData, pmcImageName, FPassword, False);
end;
Anwenden kannst du es so:
Delphi-Quellcode:
var
  timer: TPrecisionTimer;
  imgResFile: TImageResourceFile;
begin
  Image.Picture.Assign(Nil);

  timer.Start;
  imgResFile := TImageResourceFile.Create(ChangeFileExt(Application.ExeName, '.dat'), 'Thomas');
  try
    imgResFile.LoadImage(Image, 'ImageName');
    ShowMessage(Format('Total time: %s', [timer.Stop]));
  finally
    imgResFile.Free;
  end;
end;
Eine 2MB große AES verschlüsselte PNG Bilddatei aus einer Zip-Datei in ein Image laden, benötigt im Beispiele eine Gesamtladezeit von ca. 60 ms. Das ist ein brauchbarer Wert. Davon dauert die Entschlüsselung ca. 5 ms. Wenn du viele Bilder auf einmal schreiben willst, solltest du dir eine BatchAdd Funktion schreiben.

mORMot musst du nicht installierten. Es reicht aus, die entsprechenden Bibliothekspfade einzufügen. Es steht eine ausführliche Hilfe, viele Beispiele (für mORMot1) und ein freundliches Forum zur Verfügung.
Bei einer neuen Anwendung würde ich mORMot2 empfehlen. Dazu gibt es zur Zeit noch keine Hilfe. Trotzdem hier der Link zum GitHub Repro.

Bis bald...
Thomas

Geändert von mytbo ( 8. Jun 2022 um 20:09 Uhr) Grund: Ups, Funktionsergebnis vergessen!
  Mit Zitat antworten Zitat