# Tệp

> Tải ảnh và tài liệu lên Fileverity: multipart cho tệp đến 4 MB, tải lên bằng URL ký trước cho tệp đến 50 MB. Đối tượng tệp, giới hạn và thời gian lưu giữ.

Nguồn: https://fileverity.com/vi/docs/files

Ảnh và tài liệu được kiểm tra qua hai bước. Trước tiên bạn tải tệp lên bằng `POST /files` và nhận mã tệp. Sau đó bạn tạo một [lượt phân tích](https://fileverity.com/vi/docs/analyses.md) tham chiếu đến mã đó. Văn bản không cần tệp; hãy dùng [Phát hiện văn bản](https://fileverity.com/vi/docs/text-detections.md).

## Tệp được hỗ trợ

|  | Ảnh (fvimg-2) | Tài liệu (fvdoc-3) |
| --- | --- | --- |
| Định dạng | `jpeg`, `png`, `webp` | `pdf`, `docx`, `doc`, `rtf`, `odt`, `pptx`, `txt` |
| Kích thước tối đa | 10 MB | 50 MB |
| Giới hạn khác | 40 megapixel | 100 trang |

## Hai cách tải lên

| Phương thức | Yêu cầu | Kích thước | Trạng thái tệp trong phản hồi |
| --- | --- | --- | --- |
| Multipart | `multipart/form-data` với trường `file` | Tối đa 4 MB | `uploaded` |
| URL ký trước | JSON với `filename`, `content_type` và `bytes`, sau đó `PUT` các byte | Tối đa 50 MB | `pending` cho đến khi nhận được các byte |

Ảnh bị giới hạn theo kích thước ảnh nêu ở trên, bất kể bạn dùng phương thức nào.

## Tải lên bằng multipart

Gửi tệp đến 4 MB dưới dạng `multipart/form-data` trong trường `file`. Định dạng được nhận diện từ nội dung tệp, không phải từ tên hoặc kiểu được khai báo của tệp.

**Tải lên bằng multipart: cURL**

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

**Tải lên bằng 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ải lên bằng 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ải lên bằng URL ký trước

Với tệp lớn hơn 4 MB, tối đa 50 MB, hãy yêu cầu một URL tải lên, rồi gửi các byte trực tiếp đến nơi lưu trữ.

1. `POST /files` với nội dung JSON: `filename` (không bắt buộc), `content_type` và kích thước chính xác tính bằng `bytes`. Phản hồi có `status: pending` và một đối tượng `upload`.
2. `PUT` các byte của tệp tới `upload.url` kèm các header trong `upload.headers`, trước thời điểm `upload.expires_at`. Yêu cầu này không cần header `Authorization`.
3. Có thể truy vấn định kỳ `GET /files/{id}` cho đến khi `status` là `uploaded`.
4. Tạo lượt phân tích với mã tệp.

**Tải lên bằng URL ký trước: 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ải lên bằng URL ký trước: 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ải lên bằng URL ký trước: 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
  }
}
```

URL hiển thị ở đây đã được rút gọn. Hãy dùng nguyên URL trong phản hồi của bạn. Gửi header `content-type` đúng như giá trị trả về trong `upload.headers`.

### Content type được chấp nhận

| Loại | Content type |
| --- | --- |
| Ảnh | `image/jpeg`, `image/png`, `image/webp` |
| Tài liệu | `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` |

## Đối tượng tệp

| Trường | Kiểu | Mô tả |
| --- | --- | --- |
| `id` | string | Mã tệp, có tiền tố `file_`. |
| `object` | string | Luôn là `file`. |
| `filename` | string hoặc null | Tên tệp đã được làm sạch. |
| `bytes` | integer hoặc null | Kích thước tính bằng byte. |
| `content_type` | string | Content type được nhận diện hoặc được khai báo. |
| `status` | string | `pending` cho đến khi nhận đủ các byte được tải lên, sau đó là `uploaded`. |
| `livemode` | boolean | `false` với các tệp được tạo bằng khóa thử nghiệm. Tệp được tách biệt theo chế độ. |
| `created` | integer | Dấu thời gian Unix. |
| `expires_at` | integer | Dấu thời gian Unix mà sau đó tệp bị xóa. |
| `upload` | object | Chỉ có trong phản hồi cho yêu cầu tải lên dạng JSON: `url`, `method` (`PUT`), `headers` và `expires_at`. |

## Thời gian lưu giữ

- Một tệp được lưu giữ tối đa 24 giờ (xem `expires_at`) rồi bị xóa.
- Tệp bị xóa ngay khi một lượt phân tích bắt đầu từ tệp đó. Mỗi tệp đã tải lên chỉ dùng cho một lượt phân tích; hãy tải lên lại để kiểm tra lại.
- `DELETE /files/{id}` xóa tệp và các byte đã lưu của tệp trước thời hạn.
- Một không gian làm việc có thể cùng lúc chứa tối đa 50 tệp chưa hết hạn và 200 MB cho mỗi chế độ. Vượt quá mức đó, việc tải lên sẽ thất bại với `pending_files_limit` (HTTP 429, không có `retry-after`) cho đến khi các tệp được sử dụng, bị xóa hoặc hết hạn.
- Mã tệp không tồn tại, đã hết hạn hoặc đã được sử dụng sẽ trả về `file_not_found` khi bạn tạo lượt phân tích, và `not_found` khi bạn lấy hoặc xóa tệp. `file_not_found` cũng được trả về khi quá trình tải lên `upload.url` chưa hoàn tất, vì vậy hãy chờ đến khi `GET /files/{id}` hiển thị `uploaded`.

## Lỗi

| Mã lỗi | Trạng thái | Nguyên nhân |
| --- | --- | --- |
| `missing_parameter` | 400 | Không có trường `file` trong nội dung yêu cầu multipart. |
| `unsupported_media_type` | 415 | Nội dung yêu cầu không phải `multipart/form-data` cũng không phải JSON. |
| `unsupported_file` | 415 | Định dạng không được hỗ trợ. |
| `invalid_file` | 422 | Tệp trống, bị hỏng hoặc không đọc được. |
| `file_too_large` | 413 | Tệp multipart lớn hơn 4 MB, hoặc `bytes` vượt quá 50 MB. |
| `invalid_parameter` | 400 | Một trường JSON bị thiếu, sai kiểu hoặc không xác định. |
| `invalid_json` | 400 | Nội dung yêu cầu JSON không phải là một đối tượng. |
| `pending_files_limit` | 429 | Không gian làm việc có quá nhiều tệp tải lên chưa hết hạn. Phản hồi không kèm header `retry-after`. |
| `idempotency_mismatch`, `idempotency_in_progress` | 409 | `Idempotency-Key` đã được dùng với một yêu cầu khác, hoặc yêu cầu đầu tiên của khóa này vẫn đang chạy. |

> **Lưu ý.** Khóa thử nghiệm kiểm tra tính hợp lệ của các lần tải lên nhưng không lưu các byte. Yêu cầu tải lên dạng JSON với khóa thử nghiệm trả về một đối tượng tải lên thử nghiệm (`upload.test` là `true`, tệp đã ở trạng thái `uploaded`): không có gì để PUT.

