# Dateien

> Bilder und Dokumente zu Fileverity hochladen: Multipart für Dateien bis 4 MB, Upload über vorsignierte URL für Dateien bis 50 MB. Dateiobjekt, Limits und Aufbewahrung.

Quelle: https://fileverity.com/de/docs/files

Bilder und Dokumente werden in zwei Schritten geprüft. Zuerst laden Sie die Datei mit `POST /files` hoch und erhalten eine Datei-ID. Dann erstellen Sie eine [Analyse](https://fileverity.com/de/docs/analyses.md), die auf diese ID verweist. Text braucht keine Datei; verwenden Sie dafür [Textprüfungen](https://fileverity.com/de/docs/text-detections.md).

## Unterstützte Dateien

|  | Bilder (fvimg-2) | Dokumente (fvdoc-3) |
| --- | --- | --- |
| Formate | `jpeg`, `png`, `webp` | `pdf`, `docx`, `doc`, `rtf`, `odt`, `pptx`, `txt` |
| Maximale Größe | 10 MB | 50 MB |
| Weitere Limits | 40 Megapixel | 100 Seiten |

## Zwei Wege zum Hochladen

| Methode | Anfrage | Größe | Dateistatus in der Antwort |
| --- | --- | --- | --- |
| Multipart | `multipart/form-data` mit dem Feld `file` | Bis zu 4 MB | `uploaded` |
| Vorsignierte URL | JSON mit `filename`, `content_type` und `bytes`, danach ein `PUT` der Bytes | Bis zu 50 MB | `pending`, bis die Bytes eingetroffen sind |

Für Bilder gilt unabhängig von der Methode die oben genannte maximale Bildgröße.

## Multipart-Upload

Senden Sie Dateien bis 4 MB als `multipart/form-data` im Feld `file`. Das Format wird aus dem Dateiinhalt erkannt, nicht aus dem Namen oder dem angegebenen Typ.

**Multipart-Upload: cURL**

```bash
curl https://api.fileverity.com/v1/files \
  -H "Authorization: Bearer $FILEVERITY_API_KEY" \
  -F file=@claim-photo.jpg
```

**Multipart-Upload: JavaScript**

```javascript
import { readFile } from "node:fs/promises";

const form = new FormData();
form.append("file", new Blob([await readFile("claim-photo.jpg")]), "claim-photo.jpg");
const response = await fetch("https://api.fileverity.com/v1/files", {
  method: "POST",
  headers: { Authorization: "Bearer " + process.env.FILEVERITY_API_KEY },
  body: form,
});
const file = await response.json();
```

**Multipart-Upload: Python**

```python
import os
import requests

response = requests.post(
    "https://api.fileverity.com/v1/files",
    headers={"Authorization": "Bearer " + os.environ["FILEVERITY_API_KEY"]},
    files={"file": open("claim-photo.jpg", "rb")},
)
file = response.json()
```

**201 Created**

```json
{
  "id": "file_8Hk2pQ4vN9sL1xR7cW3mY6zB",
  "object": "file",
  "filename": "claim-photo.jpg",
  "bytes": 2481920,
  "content_type": "image/jpeg",
  "status": "uploaded",
  "livemode": true,
  "created": 1793700000,
  "expires_at": 1793786400
}
```

## Upload über vorsignierte URL

Fordern Sie für Dateien über 4 MB bis 50 MB eine Upload-URL an und senden Sie die Bytes dann direkt an den Speicher.

1. `POST /files` mit einem JSON-Anfragetext: `filename` (optional), `content_type` und die genaue Größe in `bytes`. Die Antwort enthält `status: pending` und ein Objekt `upload`.
2. Senden Sie die Bytes der Datei vor `upload.expires_at` per `PUT` an `upload.url` und verwenden Sie dabei die Header aus `upload.headers`. Für diese Anfrage ist kein Header `Authorization` nötig.
3. Fragen Sie optional `GET /files/{id}` ab, bis `status` den Wert `uploaded` hat.
4. Erstellen Sie die Analyse mit der Datei-ID.

**Upload über vorsignierte URL: cURL**

```bash
FILE=$(curl -s https://api.fileverity.com/v1/files \
  -H "Authorization: Bearer $FILEVERITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "contract.pdf", "content_type": "application/pdf", "bytes": 18874368}')

UPLOAD_URL=$(echo "$FILE" | jq -r .upload.url)
FILE_ID=$(echo "$FILE" | jq -r .id)

curl -X PUT "$UPLOAD_URL" \
  -H "content-type: application/pdf" \
  --data-binary @contract.pdf
```

**Upload über vorsignierte URL: JavaScript**

```javascript
import { readFile } from "node:fs/promises";

const bytes = await readFile("contract.pdf");
const response = await fetch("https://api.fileverity.com/v1/files", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.FILEVERITY_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ filename: "contract.pdf", content_type: "application/pdf", bytes: bytes.length }),
});
const file = await response.json();

await fetch(file.upload.url, { method: "PUT", headers: file.upload.headers, body: bytes });
```

**Upload über vorsignierte URL: Python**

```python
import os
import requests

data = open("contract.pdf", "rb").read()
response = requests.post(
    "https://api.fileverity.com/v1/files",
    headers={"Authorization": "Bearer " + os.environ["FILEVERITY_API_KEY"]},
    json={"filename": "contract.pdf", "content_type": "application/pdf", "bytes": len(data)},
)
file = response.json()

requests.put(file["upload"]["url"], headers=file["upload"]["headers"], data=data)
```

**201 Created**

```json
{
  "id": "file_Pw3mY6zB8Hk2pQ4vN9sL1xR7",
  "object": "file",
  "filename": "contract.pdf",
  "bytes": 18874368,
  "content_type": "application/pdf",
  "status": "pending",
  "livemode": true,
  "created": 1793700000,
  "expires_at": 1793786400,
  "upload": {
    "url": "https://example.private.blob.vercel-storage.com/f/0d6f1c2e-7b1a-4c53-9a0e-5e8a41b3c7d2?signature=...",
    "method": "PUT",
    "headers": {
      "content-type": "application/pdf"
    },
    "expires_at": 1793703600
  }
}
```

Die gezeigte URL ist gekürzt. Verwenden Sie die URL aus Ihrer Antwort unverändert. Senden Sie den Header `content-type` genau so, wie er in `upload.headers` zurückgegeben wird.

### Akzeptierte Content-Types

| Art | Content-Types |
| --- | --- |
| Bilder | `image/jpeg`, `image/png`, `image/webp` |
| Dokumente | `application/pdf`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document`, `application/msword`, `application/rtf`, `application/vnd.oasis.opendocument.text`, `application/vnd.openxmlformats-officedocument.presentationml.presentation`, `text/plain` |

## Das Dateiobjekt

| Feld | Typ | Beschreibung |
| --- | --- | --- |
| `id` | string | Datei-ID mit dem Präfix `file_`. |
| `object` | string | Immer `file`. |
| `filename` | string oder null | Bereinigter Dateiname. |
| `bytes` | integer oder null | Größe in Bytes. |
| `content_type` | string | Erkannter oder angegebener Content-Type. |
| `status` | string | `pending`, bis die hochgeladenen Bytes eingetroffen sind, danach `uploaded`. |
| `livemode` | boolean | `false` bei Dateien, die mit einem Testschlüssel erstellt wurden. Dateien sind nach Modus getrennt. |
| `created` | integer | Unix-Zeitstempel. |
| `expires_at` | integer | Unix-Zeitstempel, nach dem die Datei gelöscht wird. |
| `upload` | object | Nur in der Antwort auf eine JSON-Upload-Anfrage: `url`, `method` (`PUT`), `headers` und `expires_at`. |

## Aufbewahrung

- Eine Datei wird höchstens 24 Stunden aufbewahrt (siehe `expires_at`) und dann gelöscht.
- Eine Datei wird gelöscht, sobald eine Analyse mit ihr beginnt. Jede hochgeladene Datei kann nur für eine Analyse verwendet werden; laden Sie sie erneut hoch, um sie noch einmal zu prüfen.
- `DELETE /files/{id}` entfernt eine Datei und ihre gespeicherten Bytes früher.
- Ein Workspace kann pro Modus gleichzeitig bis zu 50 nicht abgelaufene Dateien und 200 MB enthalten. Darüber hinaus schlagen Uploads mit `pending_files_limit` fehl (HTTP 429, ohne `retry-after`), bis Dateien verwendet oder gelöscht werden oder ablaufen.
- Eine unbekannte, abgelaufene oder bereits verwendete Datei-ID liefert beim Erstellen einer Analyse `file_not_found` und beim Abrufen oder Löschen `not_found`. `file_not_found` wird auch zurückgegeben, wenn der Upload an `upload.url` noch nicht abgeschlossen ist; warten Sie daher, bis `GET /files/{id}` den Status `uploaded` zeigt.

## Fehler

| Code | Status | Ursache |
| --- | --- | --- |
| `missing_parameter` | 400 | Kein Feld `file` im Multipart-Anfragetext. |
| `unsupported_media_type` | 415 | Der Anfragetext ist weder `multipart/form-data` noch JSON. |
| `unsupported_file` | 415 | Das Format wird nicht unterstützt. |
| `invalid_file` | 422 | Die Datei ist leer, beschädigt oder nicht lesbar. |
| `file_too_large` | 413 | Eine Multipart-Datei über 4 MB oder `bytes` über 50 MB. |
| `invalid_parameter` | 400 | Ein JSON-Feld fehlt, hat den falschen Typ oder ist unbekannt. |
| `invalid_json` | 400 | Der JSON-Anfragetext ist kein Objekt. |
| `pending_files_limit` | 429 | Zu viele nicht abgelaufene Uploads im Workspace. Wird ohne Header `retry-after` gesendet. |
| `idempotency_mismatch`, `idempotency_in_progress` | 409 | Der `Idempotency-Key` wurde mit einer anderen Anfrage verwendet, oder seine erste Anfrage läuft noch. |

> **Hinweis.** Testschlüssel validieren Uploads, speichern die Bytes aber nicht. Eine JSON-Upload-Anfrage mit einem Testschlüssel liefert ein Test-Upload-Objekt (`upload.test` ist `true`, die Datei ist bereits `uploaded`): Es gibt nichts per PUT zu senden.

