Завантаження файлів (/file)
Завантаження файлів
Ендпоінт /file віддає бінарні файли, згенеровані функцією PostgreSQL, без base64 всередині
JSON-RPC. Один повернений файл надсилається як є; кілька файлів пакуються в ZIP-архів на льоту.
POST /{prefix}/{database}/fileАвтентифікація, перемикання ролі (SET LOCAL ROLE), обмеження частоти запитів і ліміт розміру
запиту ідентичні /jsonrpc. Викликач повинен мати EXECUTE на функцію.
Запит
{
"method": "api.export_documents",
"params": { "count": 3 },
"options": { "filename": "export.zip", "force_zip": false, "compression_level": 6 },
"idempotencyKey": "optional"
}| Поле | Опис |
|---|---|
method | Обов’язкове. schema.function, викликається як fn(params::jsonb). |
params | Передається функції як jsonb (за умовчанням {}). |
options.filename | Ім’я завантаженого файлу/ZIP. За умовчанням: власне ім’я файлу або export-YYYYMMDD-HHMMSS.zip. |
options.force_zip | Завжди повертати ZIP, навіть для одного файлу. |
options.compression_level | 0 = без стиснення, 1–9; за умовчанням 6. |
Контракт бази даних
Функція приймає один аргумент jsonb і повертає набір рядків. path та
content обов’язкові; mime_type і store_only необов’язкові.
CREATE FUNCTION api.export_documents(params jsonb)
RETURNS TABLE (path text, content bytea, mime_type text, store_only boolean)
LANGUAGE sql AS $$
SELECT 'report.csv', convert_to('a,b' || E'\n' || '1,2', 'UTF8'), 'text/csv', false;
$$;pathмає бути простим відносним шляхом, безпечним для розпакування в будь-якій операційній системі. Шлях відхиляється (запит завершується помилкою 500, а проблема логується), якщо він:- порожній, довший за 512 байтів, не є коректним UTF-8 або містить керівні символи;
- абсолютний (
/…,C:…) або містить зворотну скісну риску; - має порожній сегмент, або сегмент
.чи..; - має сегмент, що закінчується крапкою або пробілом (Windows їх відкидає, тож
".. "став би..); - містить
:(альтернативний потік даних NTFS) або будь-який із символів< > " | ? *; - використовує зарезервоване ім’я пристрою Windows як сегмент, з розширенням або без (
CON,PRN,AUX,NUL,COM0–COM9,LPT0–LPT9); - збігається з іншим шляхом у тій самій відповіді: імена порівнюються без урахування регістру (
a.txtіA.TXTконфліктують у macOS/Windows), а файл не може одночасно бути каталогом (aіa/b).
contentзі значеннямNULLозначає порожній файл.mime_typeвикористовується лише якщо це коректний медіатип, інакше визначається за розширенням (запасне значенняapplication/octet-stream).store_only = trueзберігає запис ZIP без стиснення (потрібно, наприклад, для файлуmimetypeв EPUB).
Відповідь
| Рядки | force_zip | Результат |
|---|---|---|
| 0 | будь-яке | 404 (помилка JSON); транзакція відкочується, тож нічого, що зробила функція, не зберігається, а idempotencyKey не витрачається |
| 1 | false | сам файл з його медіатипом |
| 1 | true | ZIP з одним записом |
| ≥ 2 | будь-яке | ZIP (application/zip) |
curl -X POST http://localhost:8080/db/my_database/file \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"method":"api.export_documents","params":{"count":3}}' -OJІм’я завантажуваного файлу (options.filename або власне ім’я файлу) санітизується: роздільники шляху, лапки та зарезервовані символи замінюються на _, а керівні символи, символи перевизначення напрямку тексту, символи нульової ширини та роздільники рядків видаляються, тож ім’я не може маскуватися під файл іншого типу.
Помилки завжди повертаються у форматі JSON, ніколи як бінарні дані: 400 некоректний запит, 401 не автентифіковано,
403 немає дозволу, 404 невідома функція або немає рядків, 413 відповідь
перевищує FILE_MAX_BYTES / FILE_MAX_ENTRIES, 500 функція завершилася помилкою або
повернула некоректну структуру.
Безпечно за конструкцією
База даних керує лише іменами файлів, вмістом і медіатипами — ніколи HTTP-заголовками. Відповіді завжди мають
Content-Disposition: attachment разом з X-Content-Type-Options: nosniff,
Content-Security-Policy: sandbox і Cache-Control: no-store, тож функція,
що повертає text/html або SVG, не може виконати скрипт в origin API. Імена файлів санітизуються.
Весь результат буферизується в пам’яті: реальна межа на запит — це FILE_MAX_BYTES плюс найбільший окремий рядок, оскільки драйвер бази даних читає рядок повністю, перш ніж можна перевірити ліміт — зважайте на кількість одночасних завантажень. Сам ZIP передається потоком.
Виявлення
Функція, що повертає набір рядків, оголошена з RETURNS TABLE (…) або параметрами OUT, які містять стовпці path і content, відображається в
capabilities з "kind": "file" та ендпоінтом /file. Специфікація OpenAPI
описує реальну операцію POST /{prefix}/{database}/file (лише для ролей, які можуть виконувати файлову
функцію); файлові функції не відкриваються як інструменти MCP.