# Fichiers

> Téléversez des images et des documents vers Fileverity : multipart pour les fichiers jusqu’à 4 Mo, téléversement par URL présignée jusqu’à 50 Mo. Objet fichier, limites et conservation.

Source : https://fileverity.com/fr/docs/files

Les images et les documents sont vérifiés en deux étapes. Vous téléversez d’abord le fichier avec `POST /files` et recevez un ID de fichier. Vous créez ensuite une [analyse](https://fileverity.com/fr/docs/analyses.md) qui fait référence à cet ID. Le texte n’a pas besoin de fichier : utilisez les [Détections de texte](https://fileverity.com/fr/docs/text-detections.md).

## Fichiers pris en charge

|  | Images (fvimg-2) | Documents (fvdoc-3) |
| --- | --- | --- |
| Formats | `jpeg`, `png`, `webp` | `pdf`, `docx`, `doc`, `rtf`, `odt`, `pptx`, `txt` |
| Taille maximale | 10 Mo | 50 Mo |
| Autres limites | 40 mégapixels | 100 pages |

## Deux façons de téléverser

| Méthode | Requête | Taille | Statut du fichier dans la réponse |
| --- | --- | --- | --- |
| Multipart | `multipart/form-data` avec le champ `file` | Jusqu’à 4 Mo | `uploaded` |
| URL présignée | JSON avec `filename`, `content_type` et `bytes`, puis un `PUT` des octets | Jusqu’à 50 Mo | `pending` jusqu’à l’arrivée des octets |

Les images sont limitées à la taille d’image indiquée ci-dessus, quelle que soit la méthode utilisée.

## Téléversement multipart

Envoyez les fichiers de 4 Mo maximum en `multipart/form-data`, dans le champ `file`. Le format est détecté à partir du contenu du fichier, et non de son nom ou de son type déclaré.

**Téléversement multipart: cURL**

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

**Téléversement multipart: 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();
```

**Téléversement multipart: 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
}
```

## Téléversement par URL présignée

Pour les fichiers de plus de 4 Mo, jusqu’à 50 Mo, demandez une URL de téléversement, puis envoyez les octets directement au stockage.

1. `POST /files` avec un corps JSON : `filename` (facultatif), `content_type` et la taille exacte en `bytes`. La réponse contient `status: pending` et un objet `upload`.
2. Envoyez par `PUT` les octets du fichier à `upload.url` avec les en-têtes de `upload.headers`, avant `upload.expires_at`. Aucun en-tête `Authorization` n’est nécessaire pour cette requête.
3. Si vous le souhaitez, interrogez `GET /files/{id}` jusqu’à ce que `status` soit `uploaded`.
4. Créez l’analyse avec l’ID du fichier.

**Téléversement par URL présignée: 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
```

**Téléversement par URL présignée: 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 });
```

**Téléversement par URL présignée: 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
  }
}
```

L’URL affichée est raccourcie. Utilisez telle quelle celle de votre réponse. Envoyez l’en-tête `content-type` exactement comme il est renvoyé dans `upload.headers`.

### Types de contenu acceptés

| Catégorie | Types de contenu |
| --- | --- |
| Images | `image/jpeg`, `image/png`, `image/webp` |
| Documents | `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` |

## L’objet fichier

| Champ | Type | Description |
| --- | --- | --- |
| `id` | chaîne | ID de fichier, avec le préfixe `file_`. |
| `object` | chaîne | Toujours `file`. |
| `filename` | chaîne ou null | Nom de fichier nettoyé. |
| `bytes` | entier ou null | Taille en octets. |
| `content_type` | chaîne | Type de contenu détecté ou déclaré. |
| `status` | chaîne | `pending` jusqu’à l’arrivée des octets téléversés, puis `uploaded`. |
| `livemode` | booléen | `false` pour les fichiers créés avec une clé de test. Les fichiers sont séparés par mode. |
| `created` | entier | Horodatage Unix. |
| `expires_at` | entier | Horodatage Unix après lequel le fichier est supprimé. |
| `upload` | objet | Présent uniquement dans la réponse à une requête de téléversement JSON : `url`, `method` (`PUT`), `headers` et `expires_at`. |

## Conservation

- Un fichier est conservé 24 heures au plus (voir `expires_at`), puis supprimé.
- Un fichier est supprimé dès qu’une analyse démarre à partir de celui-ci. Chaque fichier téléversé sert à une seule analyse ; téléversez-le à nouveau pour le vérifier de nouveau.
- `DELETE /files/{id}` supprime un fichier et ses octets stockés avant l’échéance.
- Un espace de travail peut contenir au plus 50 fichiers non expirés et 200 Mo par mode à la fois. Au-delà, les téléversements échouent avec `pending_files_limit` (HTTP 429, sans `retry-after`) jusqu’à ce que des fichiers soient utilisés, supprimés ou expirés.
- Un ID de fichier inconnu, expiré ou déjà utilisé renvoie `file_not_found` lorsque vous créez une analyse, et `not_found` lorsque vous le récupérez ou le supprimez. `file_not_found` est aussi renvoyé lorsque le téléversement vers `upload.url` n’est pas encore terminé : attendez donc que `GET /files/{id}` indique `uploaded`.

## Erreurs

| Code | Statut | Cause |
| --- | --- | --- |
| `missing_parameter` | 400 | Aucun champ `file` dans le corps multipart. |
| `unsupported_media_type` | 415 | Le corps n’est ni en `multipart/form-data` ni en JSON. |
| `unsupported_file` | 415 | Le format n’est pas pris en charge. |
| `invalid_file` | 422 | Le fichier est vide, endommagé ou illisible. |
| `file_too_large` | 413 | Un fichier multipart de plus de 4 Mo, ou `bytes` supérieur à 50 Mo. |
| `invalid_parameter` | 400 | Un champ JSON est absent, d’un type incorrect ou inconnu. |
| `invalid_json` | 400 | Le corps JSON n’est pas un objet. |
| `pending_files_limit` | 429 | Trop de téléversements non expirés dans l’espace de travail. La réponse n’est pas accompagnée d’un en-tête `retry-after`. |
| `idempotency_mismatch`, `idempotency_in_progress` | 409 | La `Idempotency-Key` a été utilisée avec une autre requête, ou sa première requête est encore en cours. |

> **Remarque.** Les clés de test valident les téléversements mais ne conservent pas les octets. Une requête de téléversement JSON avec une clé de test renvoie un objet de téléversement de test (`upload.test` vaut `true`, le fichier est déjà `uploaded`) : il n’y a rien à envoyer par PUT.

