Zum Inhalt springen

OBS/Kostenpflichtige Module/RESTServer/Datei-Download

Aus OBS Wiki
Kostenpflichtige Module

Internet-Shop
UPS
IMS Professional
SMS
Mehrlager-Verwaltung
Mehrsprachen Modul
Multilanguage Modul
EVA Marketing Tool
Termin-Projekte
Edifact-Schnittstelle
Backup Überwachung Email
OBS Geo Daten
DeliSprint / DPD
Filialen
Cashback
Moebelschnittstelle
Dokumenten Manager
DocuWare-Schnittstelle
OFML-Kalkulation
Versicherungsschaden
Gutschriftsanzeigen
Kameraverwaltung
DataInOut
OpenMasterData / IDS
Sammelpositionen



Datei-Download

Ein Endpunkt kann statt eines JSON-Körpers eine Datei ausliefern - PDF, CSV, Bilder, eine APK. Das Skript entscheidet das mit einem einzigen Aufruf, alles Weitere macht der Server: Content-Type, Dateiname, Bereichsanfragen und, bei temporären Dateien, das Aufräumen.

Anders als beim Datei-Upload braucht es dafür keine Freischaltung am Endpunkt. Ein Download entsteht dadurch, dass das Skript ihn erzeugt, nicht dadurch, dass ein Client ihn anfragt.

Die zwei Aufrufe

Aufruf Für Nach dem Senden Wiederaufsetzbar
oWriter.SendFile(cPfad, cName, cTyp) Dateien, die es schon gibt und weiter geben wird bleibt liegen ja (206/Range)
oWriter.SendTempFile(cPfad, cName, cTyp) Ergebnisse, die je Abruf entstehen wird gelöscht nein
procedure Get(oReader: TxRestReader; oWriter: TxRestWriter);
var cUid : string;
    cPfad: string;
    cName: string;
begin
    cUid := oReader.Path('uid');

    if (not BelegGehoertZuMandant(oReader.Claim('tenant'), cUid)) then begin
        oWriter.Error(404, 'NOT_FOUND', 'Beleg nicht gefunden');
        exit;
    end;

    cPfad := BelegPfad(cUid);
    cName := 'Rechnung_' + cUid + '.pdf';

    oWriter.SendFile(cPfad, cName, 'application/pdf');
end;
HINWEIS: Datei oder Felder - nicht beides. Schreibt ein Skript Felder

und ruft SendFile, ist das ein Strukturfehler wie eine unbalancierte Verschachtelung: der Server antwortet 500 und nennt die Stelle im Protokoll. Error() nach SendFile verwirft die Datei

und liefert die Fehlerhülle - die Ablehnung gewinnt.

Was der Server dazu tut

Kopfzeile Wert
Content-Type der übergebene Typ; ohne Angabe application/octet-stream
Content-Disposition attachment mit dem übergebenen Namen, nach RFC 5987 kodiert
Content-Length Länge der Datei bzw. des angeforderten Bereichs
Accept-Ranges bytes bei SendFile, none bei SendTempFile
ETag, Last-Modified nur bei SendFile, aus Grösse und Änderungszeitpunkt

Umlaute im Dateinamen sind unbedenklich. Header-Werte müssen ASCII sein, deshalb schreibt der Server zwei Angaben: filename= mit einem ausgeschriebenen Rückfallnamen (Rechnung_Müller.pdf wird zu Rechnung_Mueller.pdf) und filename*= mit dem echten Namen UTF-8-kodiert. Jeder heutige Browser nimmt den zweiten.

Der Pfad wird nicht geprüft. Ein Skript kann über Base.GFile ohnehin jede Datei des Servers lesen; eine Schranke nur an dieser Stelle würde Sicherheit vortäuschen, die es nicht gibt.

HINWEIS: Den Pfad niemals ungeprüft aus einem Client-Wert bilden. Ein

Endpunkt /public/{name}, der {name} an ein Basisverzeichnis hängt, liefert bei ..%2F..%2Fobs.ini die Konfiguration aus. Entweder eine feste Zuordnung im Skript (apk -> fester

Pfad), oder eine Tabelle mit freigegebenen Dateien - nie den Rohwert.

Wiederaufnahme abgebrochener Downloads

Bei SendFile wertet der Server den Kopf Range aus. Unterstützt wird ein Bereich je Anfrage, in den drei Formen, die Clients tatsächlich senden:

Range Bedeutung
bytes=0-1023 fester Bereich
bytes=500- ab Offset bis zum Ende
bytes=-500 die letzten 500 Bytes

Der Server antwortet dann mit 206 Partial Content und Content-Range: bytes <von>-<bis>/<gesamt>.

Fall Antwort
Kein Range-Kopf 200 mit der ganzen Datei
Mehrere Bereiche (Komma) 200 mit der ganzen Datei - die Spezifikation erlaubt das ausdrücklich
Bereich hinter dem Dateiende 416 Range Not Satisfiable mit Content-Range: bytes */<gesamt>
If-Range stimmt nicht mehr 200 mit der ganzen Datei

Warum If-Range zählt: Setzt ein Client einen Download fort und die Datei hat sich zwischenzeitlich geändert, entstünde aus zwei Ständen eine Datei, die erst beim Öffnen auffällt. Schickt der Client seinen ETag in If-Range mit und passt der nicht mehr, liefert der Server lieber alles neu.

HINWEIS: SendTempFile kann nicht fortgesetzt werden, und das ist keine

Lücke: Die Datei ist nach dem Senden gelöscht, ein zweiter Abruf fände sie nicht mehr vor - und eine Neuerzeugung lieferte andere Bytes, sobald sich die Daten geändert haben. Der Server meldet deshalb Accept-Ranges: none, damit

ein Client es gar nicht erst versucht.

Öffentliche Dateien ohne API-Key

Für Artefakte, die für jeden bestimmt sind - eine Test-APK, ein Handbuch - kann der Endpunkt ohne apikey-Header erreichbar sein. Dafür genügt Konfiguration, siehe Zugänge:

  1. Zugang mit ra_apikey = *, ra_jwt = 0, ra_cors_origin leer
  2. In RESTSRV_ACCESS nur diesen einen Endpunkt freigeben

Die zweite Zeile ist die eigentliche Grenze: Für jeden anderen Endpunkt antwortet der Server 403, auch wenn der Zugang existiert.

HINWEIS: Nur für wirklich öffentliche Dateien. Ohne API-Key kennt der

Endpunkt weder Mandant noch Rolle - es gibt nichts, woran er eine Berechtigung festmachen könnte ausser dem, was in der URL steht. Kundenbelege gehören deshalb

über den authentifizierten Weg, nicht hierher.

Für eine Android-APK kommt hinzu: Content-Type application/vnd.android.package-archive, am Gerät muss "Unbekannte Apps installieren" für den Browser freigegeben sein, und die Signatur sollte über alle Testversionen konstant bleiben - sonst gibt es beim späteren Wechsel in den Play Store auf jedem Testgerät einen Installationskonflikt.

Grenzen

Was Stand
HEAD wird nicht unterstützt (405). Browser brauchen es nicht; relevant nur für wget --continue, curl -C - und Download-Manager, die meist auf GET zurückfallen.
Mehrere Bereiche je Anfrage nicht unterstützt (ganze Datei mit 200)
Liegengebliebene Temp-Dateien Bricht der Server mitten im Senden ab, bleibt eine SendTempFile-Datei stehen. Das temporäre Verzeichnis wird von aussen geleert.
Idempotenz Eine Dateiantwort wird nicht im Idempotenz-Store festgeschrieben - sie liesse sich nicht wiedergeben. Der Idempotency-Key bleibt danach benutzbar.