Stahování souborů (/file)

3 min čtení

Stahování souborů

Endpoint /file vrací binární soubory generované PostgreSQL funkcí — bez base64 uvnitř JSON-RPC. Jeden soubor se pošle přímo, více souborů se za běhu zabalí do ZIP archivu.

POST /{prefix}/{database}/file

Autentizace, přepnutí role (SET LOCAL ROLE), rate limiting i limit velikosti požadavku jsou stejné jako u /jsonrpc. Volající musí mít EXECUTE na funkci.

Požadavek

{
  "method": "api.export_documents",
  "params": { "count": 3 },
  "options": { "filename": "export.zip", "force_zip": false, "compression_level": 6 },
  "idempotencyKey": "volitelné"
}
PolePopis
methodPovinné. schema.function, volá se jako fn(params::jsonb).
paramsPředají se funkci jako jsonb (výchozí {}).
options.filenameNázev stahovaného souboru/ZIPu. Výchozí: název souboru, nebo export-YYYYMMDD-HHMMSS.zip.
options.force_zipVždy vrátit ZIP, i pro jediný soubor.
options.compression_level0 = bez komprese, 1–9; výchozí 6.

Kontrakt na straně databáze

Funkce má jeden argument jsonb a vrací sadu řádků. Povinné jsou path a content, volitelné mime_type a 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 musí být obyčejná relativní cesta, kterou lze bezpečně rozbalit na každém operačním systému. Cesta je odmítnuta (požadavek skončí chybou 500 a problém se zaloguje), pokud:
    • je prázdná, delší než 512 bajtů, není platné UTF-8 nebo obsahuje řídicí znaky;
    • je absolutní (/…, C:…) nebo obsahuje zpětné lomítko;
    • má prázdný segment, segment . nebo ..;
    • má segment končící tečkou nebo mezerou (Windows je odstraní, takže ".. " by se stalo ..);
    • obsahuje : (alternativní datový proud NTFS) nebo některý ze znaků < > " | ? *;
    • používá jako segment rezervovaný název zařízení Windows, s příponou i bez ní (CON, PRN, AUX, NUL, COM0–COM9, LPT0–LPT9);
    • koliduje s jinou cestou ve stejné odpovědi: názvy se porovnávají bez ohledu na velikost písmen (a.txt a A.TXT se na macOS/Windows střetnou) a soubor nemůže být zároveň adresářem (a a a/b).
  • content NULL je prázdný soubor.
  • mime_type se použije jen jako validní media type, jinak se odhadne z přípony (záloha application/octet-stream).
  • store_only = true uloží položku ZIPu bez komprese (nutné např. pro soubor mimetype v EPUB).

Odpověď

Řádkůforce_zipVýsledek
0jakékoli404 (JSON chyba); transakce se vrátí zpět, takže se nic z toho, co funkce udělala, neuloží a idempotencyKey se nespotřebuje
1falsesamotný soubor s jeho media type
1trueZIP s jednou položkou
≥ 2jakékoliZIP (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

Název stahovaného souboru (options.filename nebo vlastní název souboru) se sanitizuje: oddělovače cest, uvozovky a rezervované znaky se změní na _ a řídicí znaky, znaky pro přepsání směru textu, znaky nulové šířky a oddělovače řádků se odstraní, takže se název nemůže maskovat jako jiný typ souboru.

Chyby jsou vždy JSON, nikdy binární data: 400 neplatný požadavek, 401 nepřihlášen, 403 chybí oprávnění, 404 neexistující funkce nebo žádné řádky, 413 odpověď překračuje FILE_MAX_BYTES / FILE_MAX_ENTRIES, 500 funkce selhala nebo vrátila neplatnou strukturu.

Bezpečné ze své podstaty

Databáze řídí jen názvy, obsah a media type souborů — nikdy HTTP hlavičky. Odpovědi mají vždy Content-Disposition: attachment, X-Content-Type-Options: nosniff, Content-Security-Policy: sandbox a Cache-Control: no-store, takže funkce vracející text/html nebo SVG nemůže spustit skript v originu API. Názvy souborů se sanitizují. Celý výsledek se drží v paměti: skutečná horní hranice na požadavek je FILE_MAX_BYTES plus největší jednotlivý řádek, protože databázový ovladač načte řádek celý dříve, než lze limit zkontrolovat — pamatujte na počet souběžných stahování. Samotný ZIP se streamuje.

Objevování

Funkce vracející sadu řádků, deklarovaná s RETURNS TABLE (…) nebo s parametry OUT, které zahrnují sloupce path a content, je v capabilities uvedena jako "kind": "file" s endpointem /file. OpenAPI spec obsahuje skutečnou operaci POST /{prefix}/{database}/file (jen pro role, které smí některou file funkci spustit); file funkce se nenabízejí jako MCP nástroje.