Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Internet Downloads verschiedene Get() Varianten (https://www.delphipraxis.net/196182-internet-downloads-verschiedene-get-varianten.html)

KodeZwerg 29. Apr 2018 10:53


Internet Downloads verschiedene Get() Varianten
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo Leute,

in diesem Beispiel Projekt stelle ich euch 3 verschiedene Get( URL ) Möglichkeiten vor die keine speziellen Komponenten erfordern, alles arbeitet bei Laufzeit.
Es wird HTTP und HTTPS unterstützt.
Es wird Unicode unterstützt.
Man kann auf zwei Arten speichern, entweder das Original oder die Memo-Kopie.
Es ist nicht perfekt, aber zeigt einem wie einfach man an dieses Ziel gelangen kann.
Es wird entweder eine Datei empfangen oder der HTML Sourcecode.
Anhang 48968
In der zweiten Uses Klausel ist für jede der 4 Download Varianten chronologisch die Unit enthalten die diese Funktion ermöglicht.
Bei Bedarf einfach nur die entsprechende Funktion herauskopieren und in euer Projekt bereitstellen, Uses anpassen, fertig.
Die TDownloadURL Methode springt dabei etwas aus der Reihe, dabei muss im Vorfeld ein Dateiname zum speichern angegeben werden.

Hier der Projekt Quell-Code, im Anhang das fertige Demo Projekt.

Update
- HTTP Api ActiveX hinzugefügt vs "COM nicht initialisiert" Fehler
- Zeitmessung inkl. Auswertung, momentan werden 4 Zeilen angezeigt, die ungewollten rauskommentieren/löschen
- simple Benchmark-Konfiguration hinzugefügt
- ClassNames verteilt

Delphi-Quellcode:
unit uMain;

interface

uses
  Winapi.Windows, Vcl.Controls, Vcl.StdCtrls, Vcl.Dialogs, System.Classes,
  Vcl.ExtCtrls, Vcl.Forms, System.SysUtils, System.Diagnostics;

type
  TFormMain = class(TForm)
    PanelTop: TPanel;
    PanelClient: TPanel;
    Memo1: TMemo;
    EditURL: TEdit;
    ButtonDownload: TButton;
    ButtonSaveOriginal: TButton;
    FileSaveDialog1: TFileSaveDialog;
    ButtonSaveMemo: TButton;
    ComboBoxApi: TComboBox;
    PanelBenchmark: TPanel;
    CheckBoxBenchmark: TCheckBox;
    GroupBoxBenchConfig: TGroupBox;
    ComboBoxBitsBytes: TComboBox;
    ComboBoxByteCalc: TComboBox;
    procedure ButtonDownloadClick(Sender: TObject);
    procedure ButtonSaveOriginalClick(Sender: TObject);
    procedure ButtonSaveMemoClick(Sender: TObject);
    procedure CheckBoxBenchmarkClick(Sender: TObject);
  private
    { Private declarations }
    DataString: String;
    Function GetWinInet ( Const xURL : String ) : UTF8String;
    Function GetHttpApi ( Const xURL : String ) : String;
    Function GetTDownloadURL ( Const xURL : String ) : String;
    function GetTHTTPClient ( Const xURL : String ) : String;
  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

Uses
  WinApi.WinInet,
  System.Variants, WinApi.ActiveX, System.Win.ComObj,
  Vcl.ExtActns,
  System.Net.HttpClient
  ;

{$R *.dfm}

function TFormMain.GetWinInet ( Const xURL : String ) : UTF8String;
var
  NetHandle: HINTERNET;
  UrlHandle: HINTERNET;
  Buffer: array[0..1023] of byte;
  BytesRead: dWord;
  StrBuffer: UTF8String;
begin
  Result := '';
  NetHandle := InternetOpen('Delphi-PRAXiS RockZ', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if Assigned(NetHandle) then
    try
      UrlHandle := InternetOpenUrl(NetHandle, PChar(xURL), nil, 0, INTERNET_FLAG_RELOAD, 0);
      if Assigned(UrlHandle) then
        try
          repeat
            InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
            SetString(StrBuffer, PAnsiChar(@Buffer[0]), BytesRead);
            Result := Result + StrBuffer;
          until BytesRead = 0;
        finally
          InternetCloseHandle(UrlHandle);
        end
      else
        raise Exception.CreateFmt('Cannot open URL %s', [xURL]);
    finally
      InternetCloseHandle(NetHandle);
    end
  else
    raise Exception.Create('Unable to initialize Wininet');
end;

Function TFormMain.GetHttpApi ( Const xURL : String ) : String;
var
  HTTP: OleVariant;
begin
  Result := '';
  CoInitialize(nil);
  try
    HTTP := CreateOleObject('WinHttp.WinHttpRequest.5.1');
    HTTP.Open('GET', xURL, False);
    HTTP.Send;
    Result := HTTP.ResponseText;
  finally
    HTTP := Unassigned;
    CoUninitialize;
  end;
end;

Function TFormMain.GetTDownloadURL ( Const xURL : String ) : String;
var
  dl: TDownloadURL;
  iFileHandle: Integer;
  iFileLength: Integer;
  iBytesRead: Integer;
  Buffer: PAnsiChar;
  i: Integer;
begin
  Result := '';
  if FileSaveDialog1.Execute then
  begin
    dl := TDownloadURL.Create(Self);
    try
      dl.URL := xURL;
      dl.FileName := FileSaveDialog1.FileName;
      dl.ExecuteTarget(nil);
    finally
      dl.Free;
      try
        iFileHandle := System.SysUtils.FileOpen(FileSaveDialog1.FileName, fmOpenRead);
        iFileLength := System.SysUtils.FileSeek(iFileHandle,0,2);
        System.SysUtils.FileSeek(iFileHandle,0,0);
        Buffer := System.AllocMem(iFileLength + 1);
        iBytesRead := System.SysUtils.FileRead(iFileHandle, Buffer^, iFileLength);
        Result := Buffer;
      finally
        System.SysUtils.FileClose(iFileHandle);
        System.FreeMem(Buffer);
      end;
    end;
  end;
end;

function TFormMain.GetTHTTPClient ( Const xURL : String ) : String;
var
  HttpClient: THttpClient;
  HttpResponse: IHttpResponse;
begin
  Result := '';
  HttpClient := THTTPClient.Create;
  try
    HttpResponse := HttpClient.Get( xURL );
    Result := HttpResponse.ContentAsString();
  finally
    HttpClient.Free;
  end;
end;

procedure TFormMain.ButtonDownloadClick(Sender: TObject);
var
  temp1, temp2: String;
  i : Integer;
  Watch: TStopWatch;
begin
  Memo1.Clear;
  ButtonDownload.Enabled := False;
  ButtonSaveOriginal.Enabled := False;
  ButtonSaveMemo.Enabled := False;
  PanelBenchmark.Enabled := False;
  Temp1 := EditURL.Text; Temp2 := ''; DataString := '';
  Memo1.Refresh;
  Memo1.Lines.Add('Downloading Data from ' +Temp1);
  Memo1.Lines.Add('Please Wait...');
  if CheckBoxBenchmark.Checked then
  begin
    Watch := TStopWatch.Create();
    Watch.Start;
  end;
  if Length(Temp1) > 0 then
   case ComboBoxApi.ItemIndex of
    0: DataString := GetWinInet( Temp1 );
    1: DataString := GetHttpApi( Temp1 );
    2: DataString := GetTDownloadURL( Temp1 );
    3: DataString := GetTHTTPClient( Temp1 );
   end;
  if CheckBoxBenchmark.Checked then Watch.Stop;
  if Length(DataString) > 0 then
  begin
    Memo1.Lines.Text := DataString;
    i := Length(Memo1.Lines.Text) ;
    Memo1.Lines.Add('');
    Memo1.Lines.Add('HTTP/S HTML Source from: '+Temp1);
    if Length(DataString)-i < 0 then temp2 := 'Additional added '+IntToStr(i-Length(DataString))+' extra Unicode bytes.';
    if Length(DataString)-i = 0 then temp2 := 'Plain Ascii detected.';
    if Length(DataString)-i > 0 then temp2 := 'Warning! '+IntToStr(Length(DataString)-i)+' bytes missing in Display!';
    Memo1.Lines.Add('Downloaded: '+IntToStr(Length(DataString)) +' bytes, displaying: ' +IntToStr(i)+ ' chars. '+temp2);
    if CheckBoxBenchmark.Checked then
    begin
      if (((ComboBoxBitsBytes.ItemIndex = 0)or(ComboBoxBitsBytes.ItemIndex = 1))and((ComboBoxByteCalc.ItemIndex = 0) or (ComboBoxByteCalc.ItemIndex = 1))) then
        Memo1.Lines.Add('Downloaded needed '+IntToStr(Watch.ElapsedMilliseconds)+' ms, that is '+FloatToStrF(Length(DataString) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' bytes/second <-> '+FloatToStrF((Length(DataString) / 1024) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' kbyte/s <-> '+FloatToStrF((Length(DataString) / 1024 / 1024) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' mbyte/s.');
      if (((ComboBoxBitsBytes.ItemIndex = 0)or(ComboBoxBitsBytes.ItemIndex = 2))and((ComboBoxByteCalc.ItemIndex = 0) or (ComboBoxByteCalc.ItemIndex = 1))) then
        Memo1.Lines.Add('Downloaded needed '+IntToStr(Watch.ElapsedMilliseconds)+' ms, that is '+FloatToStrF((Length(DataString)*8) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' bits/second <-> '+FloatToStrF(((Length(DataString)*8) / 1024) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' kbit/s <-> '+FloatToStrF(((Length(DataString)*8) / 1024 / 1024) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' mbit/s.');
      if (((ComboBoxBitsBytes.ItemIndex = 0)or(ComboBoxBitsBytes.ItemIndex = 1))and((ComboBoxByteCalc.ItemIndex = 0) or (ComboBoxByteCalc.ItemIndex = 2))) then
        Memo1.Lines.Add('Downloaded needed '+IntToStr(Watch.ElapsedMilliseconds)+' ms, that is '+FloatToStrF(Length(DataString) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' bytes/second <-> '+FloatToStrF((Length(DataString) / 1000) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' kbyte/s <-> '+FloatToStrF((Length(DataString) / 1000 / 1000) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' mbyte/s.');
      if (((ComboBoxBitsBytes.ItemIndex = 0)or(ComboBoxBitsBytes.ItemIndex = 2))and((ComboBoxByteCalc.ItemIndex = 0) or (ComboBoxByteCalc.ItemIndex = 2))) then
      Memo1.Lines.Add('Downloaded needed '+IntToStr(Watch.ElapsedMilliseconds)+' ms, that is '+FloatToStrF((Length(DataString)*8) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' bits/second <-> '+FloatToStrF(((Length(DataString)*8) / 1000) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' kbit/s <-> '+FloatToStrF(((Length(DataString)*8) / 1000 / 1000) / (Watch.ElapsedMilliseconds / 1000), ffFixed, 35, 2)+' mbit/s.');
{
      Memo1.Lines.Add('Above calculations based on 1024 byte = 1 kb for your pleasure 1000 byte = 1 kb follows.');
      }
    end;
    ButtonSaveOriginal.Enabled := True;
    ButtonSaveMemo.Enabled := True;
  end;
  PanelBenchmark.Enabled := True;
  ButtonDownload.Enabled := True;
end;

procedure TFormMain.ButtonSaveOriginalClick(Sender: TObject);
var
 FS: TFileStream;
 xBuf: TBytes;
 i: Integer;
begin
 if FileSaveDialog1.Execute then
 begin
   SetLength(xBuf, Length(DataString)-1);
   for i := 1 to Length(DataString) do
    xBuf[i-1] := Ord(DataString[i]);
   FS := TFileStream.Create(FileSaveDialog1.FileName, fmCreate);
   FS.WriteBuffer(xBuf, 0, Length(DataString));
   FS.Free;
 end;
end;

procedure TFormMain.ButtonSaveMemoClick(Sender: TObject);
begin
 if FileSaveDialog1.Execute then
 begin
   Memo1.Lines.SaveToFile(FileSaveDialog1.FileName);
 end;
end;

procedure TFormMain.CheckBoxBenchmarkClick(Sender: TObject);
begin
 GroupBoxBenchConfig.Enabled := CheckBoxBenchmark.Checked;
end;

end.

KodeZwerg 29. Apr 2018 23:22

AW: Internet Downloads verschiedene Get() Varianten
 
Neue Version, siehe Post #1

KodeZwerg 30. Apr 2018 10:30

AW: Internet Downloads verschiedene Get() Varianten
 
Micro-Update in Post #1

Daniel 30. Apr 2018 10:37

AW: Internet Downloads verschiedene Get() Varianten
 
Das mag für Dich ein interessantes Forschungs-Feld sein.
Aber läufst Du nicht in die falsche Richtung? Du lässt Dir den Datenstrom vom Server per String geben - wäre es nicht besser, das, was man bekommt, zuerst als Byte-Strom zu begreifen, dann etwaige Meta-Daten wie "Content-Encoding" und Konsorten auszuwerten, um am Ende einen korrekt codierten String (oder ein PNG oder ein ZIP-Archiv oder was auch immer) daraus zu erstellen?
Auch das Thema "Threading" vermisse ich ein wenig. Auf die Geschwindigkeit, in der Dir die Daten geliefert werden, hast Du i.A. keinen Einfluss. Erledigst Du den Download also im Main-Thread, steht halt erstmal Deine gesamte Anwendung.
Was zudem fehlt, ist eine Abgrenzung der verschiedenen Methoden. Warum sollte ich ein ActiveX-Objekt bemühen, wenn ich mit den NetHTTP-Komponenten oder den INDYs arbeiten könnte?

DeddyH 30. Apr 2018 10:58

AW: Internet Downloads verschiedene Get() Varianten
 
Und gehört das wirklich in diese Sparte? Es handelt sich ja nicht um ein konkretes Problem.

KodeZwerg 30. Apr 2018 11:00

AW: Internet Downloads verschiedene Get() Varianten
 
Zitat:

Zitat von Daniel (Beitrag 1400926)
Das mag für Dich ein interessantes Forschungs-Feld sein.

In der Tat, neue Sachen zu Entdecken macht Spass!
Zitat:

Zitat von Daniel (Beitrag 1400926)
Du lässt Dir den Datenstrom vom Server per String geben - wäre es nicht besser, das, was man bekommt, zuerst als Byte-Strom zu begreifen

Ich bin für Verbesserungen jederzeit bereit. Meine Binär-Tests waren erfolgreich (beim speichern wird es ein Byte-Strom).
Zitat:

Zitat von Daniel (Beitrag 1400926)
Auch das Thema "Threading" vermisse ich ein wenig. Auf die Geschwindigkeit, in der Dir die Daten geliefert werden, hast Du i.A. keinen Einfluss. Erledigst Du den Download also im Main-Thread, steht halt erstmal Deine gesamte Anwendung.

Das ist leider wahr. Ich könnte die Get() Sachen auch als Thread starten nur sehe ich darin noch keinen Unterschied für dieses Beispiel-Projekt.
Zitat:

Zitat von Daniel (Beitrag 1400926)
Was zudem fehlt, ist eine Abgrenzung der verschiedenen Methoden.

Da helf mir mal bitte auf die Sprünge, es ist doch jede Variante einzeln abgekapselt?
Zitat:

Zitat von Daniel (Beitrag 1400926)
Warum sollte ich ein ActiveX-Objekt bemühen, wenn ich mit den NetHTTP-Komponenten oder den INDYs arbeiten könnte?

Weil ich in diesem Beispiel auf andere Methoden eingehen wollte. Nur die HTML Api braucht das ActiveX.
Zugegeben meine ersten Gehversuche fanden auch mit Indy statt aber da war ich zu doof es bei Laufzeit mit SSL hinzubekommen.
Ziel dieses Beispiels soll sein das man keine Komponenten braucht die alle erst konfiguriert werden müssen bevor man in der Lage ist eine Datei oder HTML Quelltext zu laden.

Und bezüglich Content, dargestellt werden soll/kann nur "Text", halt HTML Source, runtergeladen werden kann vieles.

Zitat:

Zitat von DeddyH (Beitrag 1400930)
Und gehört das wirklich in diese Sparte? Es handelt sich ja nicht um ein konkretes Problem.

In CodeLib kann ich nichts Erstellen.

Redeemer 30. Apr 2018 11:33

AW: Internet Downloads verschiedene Get() Varianten
 
Ich find's gut, weil's die EXE nicht unnötig aufbläht, und insbesondere bei HTTPS keine Zusatzkomponenten braucht. Derzeit verwende ich dafür TDownloadURL, was bei einfachen GET-Requests geht, aber aber POST-Requests versagt.

Du könntest noch zu den einzelnen Methoden folgende Informationen erwähnen, falls du sie herausfinden kannst:
* Wie gut funktionieren sie mit POST?
* Wie gut lässt sich der Header bearbeiten? Ich benötige beispielsweise einen sehr seltsamen Request-Header.
* Seit wann geht das? TDownloadURL geht sein Jahrmillionen, das ist klar, aber seit wann geht der Rest?

Zitat:

Zitat von KodeZwerg (Beitrag 1400931)
Zitat:

Zitat von DeddyH (Beitrag 1400930)
Und gehört das wirklich in diese Sparte? Es handelt sich ja nicht um ein konkretes Problem.

In CodeLib kann ich nichts Erstellen.

Das ist eine eigene Funktion. Die Einträge sollen dann eingeordnet werden, aber der Bereich wird seit 5 Jahren nicht mehr moderiert, von daher...

KodeZwerg 30. Apr 2018 12:19

AW: Internet Downloads verschiedene Get() Varianten
 
Zitat:

Zitat von Redeemer (Beitrag 1400938)
* Seit wann geht das?

WinInet = WinInet.dll = gibt es seit InternetExplorer 3 und nimmt sich dessen Settings
HttpApi = Winhttp.dll = ab Windows XP / Windows 2000 mit SP3 - benötigt ActiveX, im Quelltext enthalten
TDownLoadURL = TDownLoadURL
THTTPClient = THTTPClient
Zitat:

Zitat von Redeemer (Beitrag 1400938)
* Wie gut funktionieren sie mit POST?

Für Deine Zwecke sollte der THTTPClient alles bieten was Du brauchst.

KodeZwerg 1. Mai 2018 18:08

AW: Internet Downloads verschiedene Get() Varianten
 
Liste der Anhänge anzeigen (Anzahl: 1)
ot
Zitat:

Zitat von Redeemer (Beitrag 1400938)

Im Anhang sind zwei Dateien, einmal das Original, da würde mich Interessieren ob es auch tatsächlich so ist, könntest Du das Überprüfen?
Die Memo.html ist einfach nur eine Memo-Kopie, ich glaub ich muss da auch noch Hand anlegen, in Memo-Anzeige sieht alles gut aus aber wenn ich Kopie von Memo in Editor lade sehen die Unicode Zeichen strange aus, wenn ich Original.html lade passt alles also muss es an der Art liegen wie Memo speichert, mit/ohne Codierung obwohl der mir "Plain Ascii" anzeigt, also genauso viele Bytes die reingekommen sind werden dargestellt ohne Zusätzliche Unicode bytes, wie Du merkst blicke ich selbst da noch nicht so ganz durch.
Es funktioniert bei mir nur mit THTTPClient. Der WinInet haut einen 400er Fehler raus und die Http Api einen 404er :roteyes:
TDownLoadURL versuche ich nach Möglichkeit auszuweichen weil der immer eine Datei anlegen will, da muss ich erst noch forschen ob ich das umgehen kann.
Anhang nur für Dich, wenn geladen sag Bescheid und ich lösche es wieder, Danke.
/ot


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:10 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz