Zum Inhalt springen

Dateien

Kurzantwort: Dateien gehen entweder als Multipart (Teil data mit dem JSON, Teil files mit den Dateien) oder Base64 im JSON an die API. Die Zuordnung läuft über den Dateinamen: Das Feld name im Datenobjekt muss dem Namen der hochgeladenen Datei entsprechen. Heruntergeladen wird über GET /{id}/file.


Terminal-Fenster
curl -X POST ".../crm/document/create/upload" \
-H "Authorization: Bearer $TOKEN" \
-F 'data={"data":{"title":"Vertrag","file":{"name":"vertrag.pdf"}},"response":["id","file"]};type=application/json' \
-F "files=@vertrag.pdf"

Im Browser:

const payload = {
data: { title: 'Vertrag', file: { name: file.name } },
response: ['id', 'file'],
};
const form = new FormData();
form.append('data', new Blob([JSON.stringify(payload)], { type: 'application/json' }));
form.append('files', file); // Feldname ist immer 'files'
await fetch(`${base}/crm/document/create/upload`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}` }, // kein Content-Type setzen!
body: form,
});

Drei Regeln, die sonst Zeit kosten:

  1. Kein Content-Type-Header von Hand. Den setzt der Browser samt Boundary; setzen Sie ihn selbst, bricht der Upload.
  2. name im Datenobjekt muss dem Dateinamen entsprechen. Darüber findet der Server die Bytes zum Feld.
  3. Der Teil heißt files, auch bei einer einzelnen Datei. Mehrere Dateien: mehrfach files anhängen, jede mit eindeutigem Namen.

Jedes Objekt im Payload, das ein Feld content trägt, wird als Datei erkannt — in beliebiger Verschachtelungstiefe:

POST /crm/document/create
{
"data": {
"title": "Vertrag",
"file": {
"name": "vertrag.pdf",
"content": "JVBERi0xLjQKJcfs…"
}
},
"response": ["id", "file"]
}

Ein data:-Präfix (data:application/pdf;base64,…) wird abgeschnitten, Sie können also direkt weiterreichen, was FileReader.readAsDataURL liefert.

Der Preis: Base64 bläht die Übertragung um rund ein Drittel auf und geht vollständig durch den Speicher. Für große Dateien nehmen Sie Multipart.

{ "file": "[object File]" }

Das entsteht, wenn ein File-Objekt in JSON.stringify landet. Eine Datei ist kein serialisierbarer Wert — entweder echtes FormData oder Base64.

const res = await fetch(`${base}/crm/document/${id}/file`, {
headers: { Authorization: `Bearer ${token}` },
});
const blob = await res.blob();

Der Endpunkt liefert die reinen Bytes. Zu beachten:

  • Der Token muss in den Header — Sie können also keine <img src> oder <a href> direkt auf diese URL setzen. Entweder Sie holen den Blob per fetch und erzeugen eine Object-URL, oder Sie lassen eine eigene Serverschicht ausliefern.
  • Die Datei wird serverseitig vollständig in den Speicher gelesen. Für sehr große Dateien ist das die praktische Grenze.

Anzeigen im Browser:

const url = URL.createObjectURL(blob);
// … verwenden …
URL.revokeObjectURL(url);

Nach dem Upload trägt das Datei-Objekt Angaben, die der Server ermittelt hat:

FeldInhalt
nameAnzeigename — den haben Sie geschickt
mimeTypeaus dem Anzeigenamen abgeleitet, sonst application/octet-stream
fileSizetatsächliche Größe der gespeicherten Datei
fileVersionKennung des gespeicherten Inhalts

Fordern Sie diese Felder im response an, wenn Sie sie in der Oberfläche brauchen:

{ "response": ["id", { "field": "file", "response": ["name", "mimeType", "fileSize"] }] }

Der mimeType kommt aus der Endung des Anzeigenamens, nicht aus dem, was Ihr Client im Multipart deklariert. Eine Datei ohne Endung landet deshalb bei application/octet-stream.

Absichtso
Inhalt ersetzenneuen Upload zum bestehenden Objekt schicken (PUT/PATCH /update/{id}/upload)
nur umbenennenname ändern, ohne Datei — der Inhalt bleibt unberührt
Datei entfernenReferenz auf null setzen (PATCH) oder das Objekt löschen

Beim Ersetzen bleibt bei auditierten Modellen die vorherige Fassung als Version erhalten; bei nicht auditierten ist sie endgültig weg.

AntwortUrsache
413 missing-filenameein Teil ohne Dateinamen
413 `duplicate-filenames…`
413 upload-failedÜbertragung abgebrochen
500 file-not-foundbeim Download: Datei fehlt im Speicher, Zugriffsfehler oder Volume nicht eingebunden
403Ihnen fehlt die Download-Rolle

Scheitert ein Upload, ist nichts gespeichert — weder Datensatz noch Datei. Wiederholen ist unbedenklich.

Wie die Ablage funktioniert, wie Versionen entstehen und was bei einem Rollback mit dem Inhalt passiert, steht unter Dateien und Storage.