Zum Inhalt springen

OBS/Kostenpflichtige Module/RESTServer/Datei-Download: Unterschied zwischen den Versionen

Aus OBS Wiki
Böhrer (Diskussion | Beiträge)
Die Seite wurde neu angelegt: „<br>Looking for online reputаtion management ѕervices? I do this job. for thiѕ work abslutly secrect work I don't sᥱt it up on my webѕite So please place…“
 
Rademacker (Diskussion | Beiträge)
Die Seite wurde neu angelegt: „{{Kostenpflichtige Module}} = 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 E…“
 
Zeile 1: Zeile 1:
<br>Looking for online reputаtion management ѕervices? I do this job. for thiѕ work abslutly secrect work I don't sᥱt it up on my webѕite So please place order by email: yangzhou2130@gmail.com or yanshaiodong@ԛq.com, skype:yarkwintеr, phone:+ 86 189-8383-9805. Guaranteed negatiνe fᥱedback removal eƄay philippineѕ website or money ƅack.<br><br>It has never bᥱen easy to deindex a bad content weƄpage. It's why number of bad content vіctimѕ have Ƅeen scamed Ьy SEO guy. Tһe fact is it іs a unique program wіch гequires ѕpecialist dedicated in this sector. My 11 years of experiences are your gurantee for the гesults.<br>online reputation management statistіcs boߋks fⲟ<br>
{{Kostenpflichtige Module}}
 
= 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 [[OBS/Kostenpflichtige Module/RESTServer/Datei-Upload|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 ==
 
{| class="wikitable"
! Aufruf !! Für !! Nach dem Senden !! Wiederaufsetzbar
|-
| <code>oWriter.SendFile(cPfad, cName, cTyp)</code> || Dateien, die es schon gibt und weiter geben wird || bleibt liegen || '''ja''' (206/Range)
|-
| <code>oWriter.SendTempFile(cPfad, cName, cTyp)</code> || Ergebnisse, die je Abruf entstehen || wird '''gelöscht''' || nein
|}
 
<syntaxhighlight lang="pascal" line>
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;
</syntaxhighlight>
 
{{Hinweis|'''Datei oder Felder - nicht beides.''' Schreibt ein Skript Felder
'''und''' ruft <code>SendFile</code>, ist das ein Strukturfehler wie eine
unbalancierte Verschachtelung: der Server antwortet '''500''' und nennt die Stelle
im Protokoll. <code>Error()</code> nach <code>SendFile</code> verwirft die Datei
und liefert die Fehlerhülle - die Ablehnung gewinnt.}}
 
== Was der Server dazu tut ==
 
{| class="wikitable"
! Kopfzeile !! Wert
|-
| Content-Type || der übergebene Typ; ohne Angabe <code>application/octet-stream</code>
|-
| Content-Disposition || <code>attachment</code> mit dem übergebenen Namen, nach RFC&nbsp;5987 kodiert
|-
| Content-Length || Länge der Datei bzw. des angeforderten Bereichs
|-
| Accept-Ranges || <code>bytes</code> bei <code>SendFile</code>, <code>none</code> bei <code>SendTempFile</code>
|-
| ETag, Last-Modified || nur bei <code>SendFile</code>, aus Grösse und Änderungszeitpunkt
|}
 
'''Umlaute im Dateinamen''' sind unbedenklich. Header-Werte müssen ASCII sein,
deshalb schreibt der Server zwei Angaben: <code>filename=</code> mit einem
ausgeschriebenen Rückfallnamen (''Rechnung_Müller.pdf'' wird zu
''Rechnung_Mueller.pdf'') und <code>filename*=</code> mit dem echten Namen
UTF-8-kodiert. Jeder heutige Browser nimmt den zweiten.
 
Der Pfad wird '''nicht geprüft'''. Ein Skript kann über <code>Base.GFile</code>
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 <code>/public/{name}</code>, der <code>{name}</code> an ein
Basisverzeichnis hängt, liefert bei <code>..%2F..%2Fobs.ini</code> 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 <code>SendFile</code> wertet der Server den Kopf <code>Range</code> aus.
Unterstützt wird '''ein''' Bereich je Anfrage, in den drei Formen, die Clients
tatsächlich senden:
 
{| class="wikitable"
! Range !! Bedeutung
|-
| <code>bytes=0-1023</code> || fester Bereich
|-
| <code>bytes=500-</code> || ab Offset bis zum Ende
|-
| <code>bytes=-500</code> || die letzten 500 Bytes
|}
 
Der Server antwortet dann mit '''206 Partial Content''' und
<code>Content-Range: bytes &lt;von&gt;-&lt;bis&gt;/&lt;gesamt&gt;</code>.
 
{| class="wikitable"
! Fall !! Antwort
|-
| Kein <code>Range</code>-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 <code>Content-Range: bytes */&lt;gesamt&gt;</code>
|-
| <code>If-Range</code> stimmt nicht mehr || '''200''' mit der ganzen Datei
|}
 
'''Warum <code>If-Range</code> 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
<code>If-Range</code> 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 <code>Accept-Ranges: none</code>, 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 <code>apikey</code>-Header erreichbar sein. Dafür genügt
Konfiguration, siehe
[[OBS/Kostenpflichtige Module/RESTServer/Zugaenge|Zugänge]]:
 
# Zugang mit <code>ra_apikey = *</code>, <code>ra_jwt = 0</code>, <code>ra_cors_origin</code> '''leer'''
# In <code>RESTSRV_ACCESS</code> '''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
<code>application/vnd.android.package-archive</code>, 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 ==
 
{| class="wikitable"
! Was !! Stand
|-
| HEAD || wird '''nicht''' unterstützt (''405''). Browser brauchen es nicht; relevant nur für <code>wget --continue</code>, <code>curl -C -</code> 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 <code>SendTempFile</code>-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 <code>Idempotency-Key</code> bleibt danach benutzbar.
|}

Aktuelle Version vom 14. September 2026, 09:07 Uhr

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.