OBS/Kostenpflichtige Module/RESTServer/Datei-Upload: Unterschied zwischen den Versionen
Böhrer (Diskussion | Beiträge) Die Seite wurde neu angelegt: „I’m Tosha from Chicoutimi studying Medicine. I did my schooling, secured 76% and hope to find someone with same interests in Squash.<br><br>My web-site ... […“ |
Die Seite wurde neu angelegt: „{{Kostenpflichtige Module}} = Datei-Upload = Ein Endpunkt nimmt Dateien nur entgegen, wenn er dafür freigeschaltet ist (<code>re_upload = 1</code>, siehe Endpunkte). Dann gilt nicht das JSON-Body-Limit von 10 MB, sondern die pro Endpunkt konfigurierte Grösse (<code>re_upload_size</code>, Standard 25 MB; Überschreitung -> '''413'''). Diese Seite beschreibt die '''Übertragung''' - was ein…“ |
||
| Zeile 1: | Zeile 1: | ||
{{Kostenpflichtige Module}} | |||
= Datei-Upload = | |||
Ein Endpunkt nimmt Dateien nur entgegen, wenn er dafür freigeschaltet ist | |||
(<code>re_upload = 1</code>, siehe | |||
[[OBS/Kostenpflichtige Module/RESTServer/Endpunkte|Endpunkte]]). Dann gilt nicht | |||
das JSON-Body-Limit von 10 MB, sondern die pro Endpunkt konfigurierte Grösse | |||
(<code>re_upload_size</code>, Standard 25 MB; Überschreitung -> '''413'''). | |||
Diese Seite beschreibt die '''Übertragung''' - was ein Client senden muss und was | |||
der Server antwortet. Was das '''Skript''' davon sieht, sind vier Aufrufe | |||
(<code>oReader.UploadPath()</code>, <code>UploadName()</code>, | |||
<code>UploadType()</code>, <code>UploadSha()</code>) und steht unter | |||
[[OBS/Kostenpflichtige Module/RESTServer/Scripting#Datei-Uploads|Scripting]]. | |||
Ein vollständiges Beispiel zeigt | |||
[[OBS/Kostenpflichtige Module/RESTServer/Beispiel6|Beispiel 6]]. | |||
{{Hinweis|Der Server legt die Datei in einem temporären Verzeichnis ab und | |||
'''verschiebt sie nicht selbst'''. Übernimmt das Skript sie nicht an ihren | |||
Zielort, bleibt sie dort liegen.}} | |||
== Einfacher Upload (multipart/form-data) == | |||
Eine Datei pro Request. Name und Content-Type stammen aus der | |||
<code>Content-Disposition</code> der Datei-Partie (<code>filename=</code>). Der | |||
Server speichert die erste Datei-Partie und ruft das Skript auf. | |||
Die '''übrigen Partien''' des Formulars - alles ohne <code>filename=</code> - | |||
liest das Skript über <code>oReader.Form('<name>')</code>. Der Wert ist UTF-8 und | |||
auf 1024 Zeichen begrenzt, der Name auf Buchstaben, Ziffern und Unterstrich | |||
reduziert. Zusatzangaben lassen sich damit im selben Request mitschicken, ohne sie | |||
an die URL zu hängen: | |||
<syntaxhighlight lang="pascal" line> | |||
procedure Post(oReader: TxRestReader; oWriter: TxRestWriter); | |||
var cPfad : string; | |||
cName : string; | |||
cTyp : string; | |||
cBemerk: string; | |||
begin | |||
cPfad := oReader.UploadPath(); | |||
cName := oReader.UploadName(); | |||
cTyp := oReader.UploadType(); | |||
cBemerk := oReader.Form('bemerkung'); // Formularfeld derselben Uebertragung | |||
// ... Datei aus cPfad ins DMS / Zielverzeichnis uebernehmen ... | |||
oWriter.Str('status' , 'ok'); | |||
oWriter.Str('dateiname', cName); | |||
oWriter.Status(201); | |||
end; | |||
</syntaxhighlight> | |||
== Resumable/Chunked Upload (Content-Range) == | |||
Für grosse Dateien überträgt der Client die Datei in Teilstücken (Chunks) mit dem | |||
Header <code>Content-Range: bytes START-END/TOTAL</code>. Der Server hängt die | |||
Chunks '''sequentiell''' an eine Session-Datei an und behandelt unvollständige | |||
Uploads selbst - das Endpunkt-Skript läuft '''erst beim vollständigen Upload''', | |||
und dann liefern ''oReader.UploadPath()'', ''UploadName()'' und ''UploadType()'' | |||
dasselbe wie beim einfachen Upload. Für die Teilstücke läuft es '''nicht'''. | |||
Name und Content-Type sind im <code>Content-Range</code>-Protokoll nicht enthalten; | |||
der Client liefert sie deshalb '''im ersten Chunk''' über den Header | |||
<code>Upload-Metadata</code> (Format wie bei tus: kommagetrennte Paare | |||
<code>schlüssel base64wert</code>, Werte Base64-kodiert): | |||
<pre> | |||
Upload-Metadata: filename ZmlsZS5wZGY=,filetype YXBwbGljYXRpb24vcGRm | |||
</pre> | |||
Erkannt werden die Schlüssel <code>filename</code> (Pflicht) und | |||
<code>filetype</code> (optional). Fehlt <code>filename</code> im ersten Chunk, | |||
wird der Upload mit '''400''' abgelehnt, ohne dass eine Datei angelegt wird. | |||
Folge-Chunks und Statusabfragen müssen die Metadaten nicht erneut senden. | |||
Ablauf (vom Client gesteuert, der Server antwortet jeweils): | |||
{| class="wikitable" | |||
! Request !! Server-Antwort | |||
|- | |||
| Erster Chunk <code>Content-Range: bytes 0-1048575/5000000</code> + <code>Upload-Metadata</code> || '''308''' Resume Incomplete + Header <code>Upload-Id</code>, <code>Upload-Offset</code>, <code>Range</code> | |||
|- | |||
| weitere Chunks (jeweils <code>Upload-Id</code> mitsenden) || '''308''' mit aktualisiertem <code>Upload-Offset</code> | |||
|- | |||
| letzter Chunk (Bereich erreicht TOTAL) || normale Skript-Antwort (z.B. '''200'''/'''201''') | |||
|- | |||
| Statusabfrage <code>Content-Range: bytes */5000000</code> || aktueller <code>Upload-Offset</code>, ohne anzuhängen (Resume) | |||
|} | |||
{| class="wikitable" | |||
! Header !! Richtung !! Bedeutung | |||
|- | |||
| Content-Range || Request || <code>bytes START-END/TOTAL</code>; <code>bytes */TOTAL</code> nur als Statusabfrage | |||
|- | |||
| Upload-Metadata || Request || tus-Metadaten im ersten Chunk: <code>filename</code> (Pflicht), <code>filetype</code> (optional), Base64-kodiert | |||
|- | |||
| Upload-Id || Request/Response || Session-Kennung. Fehlt sie im ersten Request, erzeugt der Server eine und gibt sie zurück; alle Folge-Chunks müssen sie mitsenden. | |||
|- | |||
| Upload-Offset || Response || Anzahl der bereits gespeicherten Bytes (= Startoffset des nächsten Chunks) | |||
|- | |||
| Range || Response || Bereits gespeicherter Bereich (<code>bytes=0-N</code>) | |||
|} | |||
Der Server hängt einen Chunk '''nur an, wenn START dem bereits gespeicherten Stand | |||
entspricht'''. Bei Abweichung (doppelter Chunk, Lücke) wird nicht angehängt, | |||
sondern der aktuelle ''Upload-Offset'' zurückgemeldet - der Client setzt ab dort | |||
neu auf. Eine separate Statusabfrage ist für ein Resume daher nicht zwingend nötig. | |||
Überschreitet ein Chunk bzw. die Gesamtgrösse das Limit (''re_upload_size''), | |||
antwortet der Server mit '''413 Payload Too Large'''. | |||
{{Hinweis|Der Upload-Status liegt prozesslokal im Speicher. Nach einem Neustart des Servers muss ein unvollständiger Upload neu begonnen werden.}} | |||
Aktuelle Version vom 9. September 2026, 13:32 Uhr
- A Preise aktualisieren
- C Personen übertragen
- E Kategorien verwalten
- G Kataloge verwalten
- I Merkliste übertragen
- K Varianten übertragen
- L Artikelvarianten übertragen
- M Referenzarten übertragen
- N Lagerbestände verwalten
- U Bestellungen einlesen
- V leere Passworte füllen
- W Update-Informationen zurücksetzen
- X Konfiguration
- Z Protokoll
Datei-Upload
Ein Endpunkt nimmt Dateien nur entgegen, wenn er dafür freigeschaltet ist
(re_upload = 1, siehe
Endpunkte). Dann gilt nicht
das JSON-Body-Limit von 10 MB, sondern die pro Endpunkt konfigurierte Grösse
(re_upload_size, Standard 25 MB; Überschreitung -> 413).
Diese Seite beschreibt die Übertragung - was ein Client senden muss und was
der Server antwortet. Was das Skript davon sieht, sind vier Aufrufe
(oReader.UploadPath(), UploadName(),
UploadType(), UploadSha()) und steht unter
Scripting.
Ein vollständiges Beispiel zeigt
Beispiel 6.
verschiebt sie nicht selbst. Übernimmt das Skript sie nicht an ihren
Zielort, bleibt sie dort liegen.Einfacher Upload (multipart/form-data)
Eine Datei pro Request. Name und Content-Type stammen aus der
Content-Disposition der Datei-Partie (filename=). Der
Server speichert die erste Datei-Partie und ruft das Skript auf.
Die übrigen Partien des Formulars - alles ohne filename= -
liest das Skript über oReader.Form('<name>'). Der Wert ist UTF-8 und
auf 1024 Zeichen begrenzt, der Name auf Buchstaben, Ziffern und Unterstrich
reduziert. Zusatzangaben lassen sich damit im selben Request mitschicken, ohne sie
an die URL zu hängen:
procedure Post(oReader: TxRestReader; oWriter: TxRestWriter);
var cPfad : string;
cName : string;
cTyp : string;
cBemerk: string;
begin
cPfad := oReader.UploadPath();
cName := oReader.UploadName();
cTyp := oReader.UploadType();
cBemerk := oReader.Form('bemerkung'); // Formularfeld derselben Uebertragung
// ... Datei aus cPfad ins DMS / Zielverzeichnis uebernehmen ...
oWriter.Str('status' , 'ok');
oWriter.Str('dateiname', cName);
oWriter.Status(201);
end;Resumable/Chunked Upload (Content-Range)
Für grosse Dateien überträgt der Client die Datei in Teilstücken (Chunks) mit dem
Header Content-Range: bytes START-END/TOTAL. Der Server hängt die
Chunks sequentiell an eine Session-Datei an und behandelt unvollständige
Uploads selbst - das Endpunkt-Skript läuft erst beim vollständigen Upload,
und dann liefern oReader.UploadPath(), UploadName() und UploadType()
dasselbe wie beim einfachen Upload. Für die Teilstücke läuft es nicht.
Name und Content-Type sind im Content-Range-Protokoll nicht enthalten;
der Client liefert sie deshalb im ersten Chunk über den Header
Upload-Metadata (Format wie bei tus: kommagetrennte Paare
schlüssel base64wert, Werte Base64-kodiert):
Upload-Metadata: filename ZmlsZS5wZGY=,filetype YXBwbGljYXRpb24vcGRm
Erkannt werden die Schlüssel filename (Pflicht) und
filetype (optional). Fehlt filename im ersten Chunk,
wird der Upload mit 400 abgelehnt, ohne dass eine Datei angelegt wird.
Folge-Chunks und Statusabfragen müssen die Metadaten nicht erneut senden.
Ablauf (vom Client gesteuert, der Server antwortet jeweils):
| Request | Server-Antwort |
|---|---|
Erster Chunk Content-Range: bytes 0-1048575/5000000 + Upload-Metadata |
308 Resume Incomplete + Header Upload-Id, Upload-Offset, Range
|
weitere Chunks (jeweils Upload-Id mitsenden) |
308 mit aktualisiertem Upload-Offset
|
| letzter Chunk (Bereich erreicht TOTAL) | normale Skript-Antwort (z.B. 200/201) |
Statusabfrage Content-Range: bytes */5000000 |
aktueller Upload-Offset, ohne anzuhängen (Resume)
|
| Header | Richtung | Bedeutung |
|---|---|---|
| Content-Range | Request | bytes START-END/TOTAL; bytes */TOTAL nur als Statusabfrage
|
| Upload-Metadata | Request | tus-Metadaten im ersten Chunk: filename (Pflicht), filetype (optional), Base64-kodiert
|
| Upload-Id | Request/Response | Session-Kennung. Fehlt sie im ersten Request, erzeugt der Server eine und gibt sie zurück; alle Folge-Chunks müssen sie mitsenden. |
| Upload-Offset | Response | Anzahl der bereits gespeicherten Bytes (= Startoffset des nächsten Chunks) |
| Range | Response | Bereits gespeicherter Bereich (bytes=0-N)
|
Der Server hängt einen Chunk nur an, wenn START dem bereits gespeicherten Stand entspricht. Bei Abweichung (doppelter Chunk, Lücke) wird nicht angehängt, sondern der aktuelle Upload-Offset zurückgemeldet - der Client setzt ab dort neu auf. Eine separate Statusabfrage ist für ein Resume daher nicht zwingend nötig.
Überschreitet ein Chunk bzw. die Gesamtgrösse das Limit (re_upload_size), antwortet der Server mit 413 Payload Too Large.