Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Delphi Indy Datei via URL downloaden und temporär öffnen? (https://www.delphipraxis.net/200914-delphi-indy-datei-via-url-downloaden-und-temporaer-oeffnen.html)

CG2003 6. Jun 2019 14:42

Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Hallo zusammen,

ich lade momentan folgendermassen via Indy eine Datei herunter:

Code:
IdHTTP.Get(Host + '/restapi/index.php/document/' + String(lvSearch.Items.Item[i].Data) + '/content', Stream);
Stream.SaveToFile(GetSpecialFolder(CSIDL_APPDATA) + '\test.msg');
Das funktioniert soweit auch, dennoch habe ich hierzu zwei Fragen:

1.
Ich muss ja derzeit für das SaveToFile die Dateiendung und vorzugsweise auch den Dateinamen vorab wissen.
Besteht die Möglichkeit, dass mir die Indy Komponente das vorab liefern kann so dass die Datei (Dateityp hinter dem Download kann sich ändern) immer mit korrekter Endung gespeichert wird?

2.
Gibt es eine Möglichkeit, die heruntergeladene Datei nicht zu speichern und quasi "nur" temporär direkt aus dem Stream heraus zu öffnen?
Falls nicht, besteht die Möglichkeit zu erkennen ob die heruntergeladene Datei noch offen ist und falls nicht, diese dann zu löschen?


Zum Hintergrund:
Ich bastel gerade an einem Outlook Addin, welches in einem DMS nach Dokumenten sucht.
Durch Doppelklick auf das Dokument, soll dieses vom DMS via URL heruntergeladen und geöffnet werden.
Aber lediglich nur zur Anzeige.
Wenn ich das Dokument wieder schließe, dann soll die temporär heruntergeladene Datei gelöscht werden.


Vielen Dank im Voraus schon mal für Eure Hilfe.
Falls Ihr mehr Informationen benötigt, so meldet Euch gern.

Delphi.Narium 6. Jun 2019 15:15

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Sowas?
Delphi-Quellcode:
var
  sl : TStringList;
  sUrl : String;
begin
  sl := TStringList.Create;
  sUrl := Format('%s/restapi/index.php/document/%s/content', [Host,String(lvSearch.Items.Item[i].Data)]);
  IdHTTP.Get(sUrl, Stream);
  Stream.Position := 0;
  sl.LoadFromStream(Stream);
  ShowMessage(sl.Text);
  sl.Free;
end;
Statt TStringList kannst Du auch alles andere nehmen, was über eine Methode LoadFromStream o. ä. verfügt. Das könnte z. B. ein TMemo sein. 'nen Dateinamen brauchst Du für sowas nicht.

Willst Du nur Infos über einen "demnächst" durchzuführenden Download, kannst Du auch eine Head-Abfrage machen.

Aus RawHeaders ('ner TStringList) kannst Du Dir dann die gewünschte Info raussuchen.

Dann wird aus Deiner Routine sowas in der Art:
Delphi-Quellcode:
var
  sl : TStringList;
  sUrl : String;
  sDatei : String;
begin
  sl := TStringList.Create;
  sUrl := Format('%s/restapi/index.php/document/%s/content', [Host,String(lvSearch.Items.Item[i].Data)]);
  IdHTTP.Head(sUrl);
  // Wenn das Laden des Inhaltes nicht direkt per Stream möglich sein sollte:
  sDatei := Format('%s\%s',[GetSpecialFolder(CSIDL_APPDATA),IdHTTP.Response.RawHeaders.Values['Filename']]); // oder wie das da im konkreten Einzelfall heißen mag.
  IdHTTP.Get(sUrl, Stream);
  Stream.SaveToFile(sDatei);
  sl.LoadFromFile(sDatei);
  ShowMessage(sl.Text);
  sl.Free;
  DeleteFile(sDatei);
end;
Ungetestet hingedaddelt.

mjustin 6. Jun 2019 16:58

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Indy enthält eine Methode GetMIMEDefaultFileExt mit der sich zu einem MIME-Typ die Dateiendung ermitteln lässt:

Delphi-Quellcode:
uses
  ..., IdGlobalProtocols;

function GetExtension(const AMIMEType: string);
begin
  Result := GetMIMEDefaultFileExt(AMIMEType);
end
Beispiel: GetExtension('application/pdf') -> Endung '.pdf'

Quelle: https://stackoverflow.com/a/44954955/80901, mit Hinweise zur Optimierung.

Den MIME-Typ sendet der HTTP Server mit der Antwort als Response Header mit, d.h. die Funktion arbeitet auch dann wenn der URL selbst keine Datei-Endung enthält.

CG2003 6. Jun 2019 20:52

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Erstmal Danke für Eure Antworten.
Scheinbar gibt mir aber der Server keinen MIME Typ zurück.
Kann man das irgendwie prüfen?

Delphi.Narium 7. Jun 2019 09:01

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Was steht denn in IdHTTP.Response.RawHeaders.Text?

Ist da nix auswertbares bei?

Könntest Du den Inhalt davon eventuell mal hier posten?

CG2003 7. Jun 2019 09:35

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Nee da ist leider nix dabei:

Code:
Date: Fri, 07 Jun 2019 08:34:41 GMT
Server: Apache/2.4.29 (Ubuntu)
Keep-Alive: timeout=5, max=99
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8

Kann es evtl. an der Apache Konfiguration liegen?

mjustin 7. Jun 2019 10:05

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Zitat:

Zitat von CG2003 (Beitrag 1434118)
Code:
Date: Fri, 07 Jun 2019 08:34:41 GMT
Server: Apache/2.4.29 (Ubuntu)
Keep-Alive: timeout=5, max=99
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8

Kann es evtl. an der Apache Konfiguration liegen?

Das ist anzunehmen. Allerdings wenn das der richtige Response-Header ist und darin Content-Type: text/html; charset=UTF-8 steht, dann würde beim Download mit einem Webbrowser dieser anbieten, das Dokument als HTML Datei zu speichern. Wenn der Browser HTML erkennt, es aber z.B. ein PDF ist, läuft etwas schief auf dem Server.

Was passiert denn wenn man mit dem Browser eine Datei im CMS auswählt / anklickt und diese herunterlädt, zeigt der Browser dann die richtige Dateierweiterung im Download-Dialog an, oder nur HTML?

Wenn es im Browser anders ist als beim Indy-HTTP Download, dann kann es an einem fehlerhaft konfigurierten Request aus Indy liegen...

Delphi.Narium 7. Jun 2019 10:09

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Nimm doch Content-Type: text/html; charset=UTF-8.

Davon brauchst Du "text/html", das spendierst Du dann GetMIMEDefaultFileExt und die gesuchte Dateiendung sollte da rauskommen.

Ungetestet sowas in der Art:
Delphi-Quellcode:
function GetExtension(const AMIMEType: string);
begin
  Result := GetMIMEDefaultFileExt(AMIMEType);
end;

...

var
  sType : String;
  i : Integer;
begin
  sType := IdHTTP.Response.RawHeaders.Values['Content-Type'];
  i := Pos(' ',sType);
  if i > 0 then sType := Copy(sType,1,i - 1);
  ShowMessage(GetExtension(sType));
  ...
end;

mjustin 7. Jun 2019 10:18

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1434121)
Nimm doch Content-Type: text/html; charset=UTF-8.

Davon brauchst Du "text/html", das spendierst Du dann GetMIMEDefaultFileExt und die gesuchte Dateiendung sollte da rauskommen.

Ja, für HTML wäre e .html (früher .htm). Im Originalpost steht, dass in der URL kein Dateiname enthalten ist und der Dateityp sich ändern kann. Wenn Indy konstant text/hmtl erhält, kommt von GetMIMEDefaultFileExt dann immer nur .html.

Delphi.Narium 7. Jun 2019 10:31

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Wir wissen aber nicht, ob Indy konstant text/hmtl erhält, sondern nur aus einem Beispiel, dass es bei diesem text/hmtl erhält.

Zuerst wäre also mal zu prüfen, ob Indy immer text/html enthält oder nur bei bestimmten Dokumenten.

Und wenn man sich mal hier von der Delphipraxis die Headerdaten anschaut, so bekommt man auch keinen Mime-Type, sondern "nur" den Content-Type von 'nem Apache-Server geliefert. Und der passt immer zum Typ der geladenen Daten, wie z. B.:
Code:
text/html; charset=iso-8859-1
text/css
application/javascript
image/png
image/gif
...
Der Apache arbeitet schon durchaus korrekt.

Jedenfalls entnehme ich den Dateityp seit gefühlten Jahrzehnten regelmäßig dem Content-Type und das klappte bisher immer, egal welcher Webserver auf der "Gegenseite" steht.

Der TE sollte also bitte erstmal prüfen, ob er immer nur text/html als Content-Type geliefert bekommt oder nur bei HTML.

CG2003 7. Jun 2019 10:34

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Hallo zusammen,

ich habe gerade mal nachgeschaut, ich bekomme immer text/html, egal welcher Dateityp dahinter steckt.
In meinem Header den ich oben gespostet habe, hatte ich versucht eine PDF Datei herunterzuladen.
Im Browser funktioniert alles einwandfrei, da kann ich eine Datei problemlos runterladen und sie wird auch mit korrektem Namen und Endung gespeichert.

Alles schon sehr merkwürdig.

Delphi.Narium 7. Jun 2019 10:56

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Um welche Dateitypen handelt es sich, kannst Du das einschränken auf z. B. nur HTML, PDF ... oder musst Du mit "kann alles sein, was es so gibt" rechnen?

Ist es eingeschränkt auf bestimmte Typen, kannst Du das selbst auswerten.

Du hast nach dem Get per Indy ja 'nen Stream.

Die ersten 4 Byte einer PDF sind immer "%PDF", das kann man einfach abfragen.

HTML beginnt mit <html, xml mit <xml ...

Es kommt also auf die "Menge der möglichen Dateitypen" an, ob es nicht einfacher ist das "eben mal" selbst zu prüfen.

Der FireFox hat 'ne Webkonsole, in der kann man sich die Headerdaten übersichtlich anschauen, kannst Du den bei Dir auch verwenden? Notfalls halt die Url, die Du Indy beim Get übergibst, mal per Copy&Paste dem Browser spendieren und den mal die Headerdaten anzeigen lassen.

Bei anderen Browsern müsstest Du mal schauen, ob sie 'ne ähnliche Option bieten.

Ansonsten:

https://elmar-eigner.de/_freetools/h...r-auslesen.php
https://www.gaijin.at/de/tools/webserver-spy

Schau bitte mal nach, welche Webseite den Mime-Type nicht in Content-Type liefert.

Literatur zur Definiton dessen, was im Header wie zu stehen hat: https://www.w3.org/Protocols/rfc2616....html#sec14.17

mjustin 7. Jun 2019 10:57

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Zitat:

Zitat von CG2003 (Beitrag 1434128)
ich habe gerade mal nachgeschaut, ich bekomme immer text/html, egal welcher Dateityp dahinter steckt.
In meinem Header den ich oben gespostet habe, hatte ich versucht eine PDF Datei herunterzuladen.
Im Browser funktioniert alles einwandfrei, da kann ich eine Datei problemlos runterladen und sie wird auch mit korrektem Namen und Endung gespeichert.

Ist JavaScript im Spiel? Testweise muss man dazu die URL im Browser nicht anklicken, sondern sie in einem neuen Fenster (oder sogar einem anderen Browser) direkt im Adressfeld eingeben. So kann man den GET Request einfach testen, ob er dann immer noch funktioniert.

Wenn es dann auch funkioniert, tippe ich stark auf ein Problem mit dem Indy Request. Das zu untersuchen ist aber auch möglich. Man muss dazu den HTTP Traffic des Browsers aufzeichnen. Über F12 (Entwicklermodus) kann man loggen, was im Detail an den CMS Server gesendet wird, und welcher MIME-Typ im Response Header zurückkommt. Hat man den funktionierenden Request, muss man diesen mit Indy nachbauen.

Wie sieht der Indy Request im HTTP Header aus? Eventuell fällt dort etwas auf.

Delphi.Narium 7. Jun 2019 11:04

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Was auch noch sein könnte, dass im Browser zuerst 'ne HTML-Seite angefordert wird und dann per HTTP-Code 302 (o. ä.) eine Weiterleitung zur PDF erfolgt, die dann mit 'nem passenden Content-Type geliefert wird.

Wie ist die Indykomponete konfiguriert? Folgt sie Redirects? Wenn nein, das mal bitte ändern.

CG2003 17. Jun 2019 16:37

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Hallo zusammen,

danke für Eure Denkanstöße. Problem gelöst. Es lag an einer fehlerhaften Apache Konfiguration.

Eine Frage habe ich noch offen. Wenn ich jetzt mittels Indy Response Stream eine Datei herunterlade, kann ich diese irgendwie "nur" temporär herunterlacen, also nur solange auf der Platte belassen so lange sie geöffnet ist?
Evtl. beim Download und Speichern der Datei ein Windows-Flag "zu löschen" oder ähnliches zu setzen?

hoika 17. Jun 2019 17:01

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Hallo,
Zitat:

so lange sie geöffnet ist
danach DeleteFile benutzen, oder habe ich das was ganz falsch verstanden?

CG2003 17. Jun 2019 17:11

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Das geht aber nur in einem Loop solange meine Applikation läuft.
Ich würde gern die Datei zum Löschen markieren so dass - sie sobald sie nicht mehr geöffnet ist - gelöscht wird.

Luckie 17. Jun 2019 18:11

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Wie wäre es mit CreateFile und dem Flat FILE_FLAG_DELETE_ON_CLOSE?

Zitat:

The file is to be deleted immediately after all of its handles are closed, which includes the specified handle and any other open or duplicated handles.

If there are existing open handles to a file, the call fails unless they were all opened with the FILE_SHARE_DELETE share mode.

Subsequent open requests for the file fail, unless the FILE_SHARE_DELETE share mode is specified.

CG2003 17. Jun 2019 19:27

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Hallo Michael,

das klingt an sich nicht schlecht aber...

ich erstelle momentan die Datei aus einem Stream:

Code:
procedure TfrmSearch.lvSearchDblClick(Sender: TObject);
begin
   Stream := TMemoryStream.Create;
    try
      IdHTTP.HandleRedirects := True;
        IdHTTP.Get(Host + '/restapi/index.php/document/' + String(lvSearch.Items.Item[i].Data) + '/content', Stream);
        Stream.SaveToFile(GetSpecialFolder(CSIDL_APPDATA) + '\' + ResponseFileName);
    except
      on E:Exception do begin
          ShowMessage('Fehler: ' + E.Message);
        end;
    end;
    Stream.Free;
end;
CreateFile jedoch erstellt die Datei ja auch und ich kann da ja nur einen Dateinamen übergeben, jedoch keinen Stream oder habe ich was übersehen?

Luckie 17. Jun 2019 19:30

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Du bekommst ein Dateihandle zurück. Eventuell kannst du den an den Stream übergeben.

mjustin 18. Jun 2019 18:52

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Zitat:

Zitat von Luckie (Beitrag 1434865)
Wie wäre es mit CreateFile und dem Flat FILE_FLAG_DELETE_ON_CLOSE?

Zitat:

...
Subsequent open requests for the file fail, unless the FILE_SHARE_DELETE share mode is specified.

Diese Einschränkung bedeutet: die temporäre Datei, die mit FILE_FLAG_DELETE_ON_CLOSE geöffnet wird, kann nur dann von der anderen Anwendung (z.B. PDF Reader) geöffnet werden, wenn diese auch beim Öffnen das Flag FILE_SHARE_DELETE angibt. Beschrieben hier:

https://stackoverflow.com/questions/...-on-close-flag

Es kommt dann vermutlich die Fehlermeldung: "The process cannot access the file because it is being used by another process."

CG2003 26. Jun 2019 09:51

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Hallo zusammen,

ich lade die Datei momentan so herunter in einen MemoryStream:

Delphi-Quellcode:
    Stream := TMemoryStream.Create;
    IdHTTP.HandleRedirects := True;
    IdHTTP.Get('http://192.168.178.244/restapi/index.php/document/4/content', Stream);
    Memo1.Text := IdHTTP.Response.RawHeaders.Text;
    ResponseFileNameTemp := StringReplace(IdHTTP.Response.RawHeaders.Values['Content-Disposition'], 'attachment; filename="', '', [rfReplaceAll]);
    ResponseFileName := StringReplace(ResponseFileNameTemp, '"', '', [rfReplaceAll]);
    Stream.SaveToStream(CreateUnbuffedFilestream(GetSpecialFolder(CSIDL_APPDATA) + '\' + ResponseFileName));

Die Funktion "CreateUnbufferedFilestream" sieht so aus:

Delphi-Quellcode:
function CreateUnbuffedFilestream(const filename:string):TStream;
var FileHandle, hnd : THandle;
begin
  FileHandle := CreateFile(PChar(filename), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_DELETE_ON_CLOSE, 0);
  if hnd <> INVALID_HANDLE_VALUE then begin
    result := THandleStream.Create(FileHandle);
    ShellExecute(Application.Handle, 'open', PChar(filename), nil, nil, sw_show);
  end else
    result := nil;
end;

Das funktioniert soweit jedoch kann die Dtaei mittels ShellExecute nicht geöffnet werden. Bekomme da immer die Meldung dass die Datei in Benutzung ist.

mjustin 26. Jun 2019 16:02

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Zitat:

Zitat von CG2003 (Beitrag 1435368)
Das funktioniert soweit jedoch kann die Dtaei mittels ShellExecute nicht geöffnet werden. Bekomme da immer die Meldung dass die Datei in Benutzung ist.

Das könnte das in Beitrag #21 vernutete Verhalten von Windows sein. Die Anwendung, die mit ShellExecute gestartet wird, benutzt nicht das laut Doku für FILE_FLAG_DELETE_ON_CLOSE notwendige Flag FILE_SHARE_DELETE.

CG2003 26. Jun 2019 17:24

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Hmm schade, in meinem Beispiel war das jetzt der Adobe Reader.
Gibt es denn eine andere Möglichkeit???

mjustin 27. Jun 2019 10:39

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Zitat:

Zitat von CG2003 (Beitrag 1435421)
Hmm schade, in meinem Beispiel war das jetzt der Adobe Reader.
Gibt es denn eine andere Möglichkeit???

Nicht, wenn man keine Änderungen an den vom ShellExecute zum Öffnen verwendeten Flags vornehmen kann...

peterbelow 27. Jun 2019 11:19

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von CG2003 (Beitrag 1435421)
Hmm schade, in meinem Beispiel war das jetzt der Adobe Reader.
Gibt es denn eine andere Möglichkeit???

Sieh Dir mal das Archiv in der Anlage an. Extrahiere den Inhalt in ein Verzeichnis, in dem der Compiler die Files finden kann, füge PB.TmporaryFilesManagerU zu deiner Uses-Klausel hinzu. Dann kannst Du mit

Delphi-Quellcode:
TemporaryFilesManager.Add(aFilenameWithFullPath);
Eine Datei zu einer internen Liste hinzufügen, die dann beim Schließen der Anwendung automatisch gelöscht werden. Allerdings geht das nur, wenn die Datei nicht auch noch in einer externen Anwendung geöffnet ist.

CG2003 3. Jul 2019 11:45

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Hallo Peter,

danke, das funktioniert perfekt.
Schön wäre jetzt noch eine Möglichkeit, die temporäre(n) Datei(en) zu löschen die nicht gelöscht werden konnten, weil die
Applikation noch offen war, aber das könnte man ja mit einem geplanten Task evtl. lösen.

peterbelow 3. Jul 2019 11:54

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Zitat:

Zitat von CG2003 (Beitrag 1435903)
Hallo Peter,

danke, das funktioniert perfekt.
Schön wäre jetzt noch eine Möglichkeit, die temporäre(n) Datei(en) zu löschen die nicht gelöscht werden konnten, weil die
Applikation noch offen war, aber das könnte man ja mit einem geplanten Task evtl. lösen.

Es gibt schon einen Grund, wieso das temp-Verzeichnis im Laufe der Zeit so viel Schrott ansammelt :wink:.

Was Du relativ leicht machen kannst ist folgendes: lege die temporären Dateien in einem Unterordner des temp-Verzeichnisses ab. Den kannst Du dann einfach bei jedem Programmstart leerputzen, damit bleiben die nicht gelöschten Dateien nur bis zur nächsten Session liegen.

CG2003 3. Jul 2019 11:58

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Klasse Tip, genau so mache ich das mal. Danke Dir!

HolgerX 3. Jul 2019 12:54

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Hmm..

Alternative:

MoveFileEx(PChar(YourFileToDelete), nil, MOVEFILE_DELAY_UNTIL_REBOOT)

Erzeuge deine Dateien ganz normal und rufe dann direkt hierfür die obrige MoveFileEx funktion auf.

Nun dein ShellExecute und die Datei kann geöffnet/Angezeigt werden.

Beim nächsten Reboot von Windows werden diese Dateien gelöscht.
Ist nicht schön, dass dies erst beim Reboot erfolgt, aber immerhin.. ;)

CG2003 3. Jul 2019 13:17

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Prinzipiell gute Idee, aber aus der Doku:

Code:
This value can be used only if the process is in the context of a user who belongs to the administrator group or the LocalSystem account.

HolgerX 3. Jul 2019 14:54

AW: Delphi Indy Datei via URL downloaden und temporär öffnen?
 
Hmm..

OK, sorry, die kleine Zeile ist mir in der API-Beschreibung von MS unter gegangen.. ;)


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:46 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