Pobieranie plików (/file)

3 min czytania

Pobieranie plików

Endpoint /file udostępnia pliki binarne generowane przez funkcję PostgreSQL — bez base64 wewnątrz JSON-RPC. Pojedynczy zwrócony plik jest wysyłany bez zmian; wiele plików jest pakowanych w locie do archiwum ZIP.

POST /{prefix}/{database}/file

Uwierzytelnianie, przełączanie roli (SET LOCAL ROLE), rate limiting i limit rozmiaru żądania są identyczne jak w /jsonrpc. Wywołujący musi mieć EXECUTE na funkcji.

Żądanie

{
  "method": "api.export_documents",
  "params": { "count": 3 },
  "options": { "filename": "export.zip", "force_zip": false, "compression_level": 6 },
  "idempotencyKey": "optional"
}
PoleOpis
methodWymagane. schema.function, wywoływana jako fn(params::jsonb).
paramsPrzekazywane do funkcji jako jsonb (domyślnie {}).
options.filenameNazwa pobieranego pliku/ZIP-a. Domyślnie: nazwa samego pliku lub export-YYYYMMDD-HHMMSS.zip.
options.force_zipZawsze zwraca ZIP, nawet dla pojedynczego pliku.
options.compression_level0 = tylko przechowywanie, 1–9; domyślnie 6.

Kontrakt z bazą danych

Funkcja przyjmuje jeden argument jsonb i zwraca zbiór wierszy. path i content są wymagane; mime_type i store_only są opcjonalne.

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 musi być zwykłą ścieżką względną, bezpieczną do rozpakowania w każdym systemie operacyjnym. Ścieżka jest odrzucana (żądanie kończy się błędem 500, a problem jest logowany), jeśli:
    • jest pusta, dłuższa niż 512 bajtów, nie jest poprawnym UTF-8 lub zawiera znaki sterujące;
    • jest bezwzględna (/…, C:…) lub zawiera odwrotny ukośnik;
    • ma pusty segment albo segment . lub ..;
    • ma segment kończący się kropką lub spacją (Windows je usuwa, więc ".. " stałoby się ..);
    • zawiera : (alternatywny strumień danych NTFS) lub którykolwiek ze znaków < > " | ? *;
    • używa zarezerwowanej nazwy urządzenia Windows jako segmentu, z rozszerzeniem lub bez (CON, PRN, AUX, NUL, COM0–COM9, LPT0–LPT9);
    • koliduje z inną ścieżką w tej samej odpowiedzi: nazwy są porównywane bez rozróżniania wielkości liter (a.txt i A.TXT kolidują w macOS/Windows), a plik nie może być jednocześnie katalogiem (a i a/b).
  • content równe NULL oznacza pusty plik.
  • mime_type jest używany tylko wtedy, gdy jest poprawnie sformułowanym typem mediów; w przeciwnym razie jest zgadywany na podstawie rozszerzenia (domyślnie application/octet-stream).
  • store_only = true zapisuje wpis ZIP bez kompresji (potrzebne np. dla pliku mimetype w EPUB).

Odpowiedź

Wierszeforce_zipWynik
0dowolnie404 (błąd JSON); transakcja jest wycofywana, więc nic, co zrobiła funkcja, nie zostaje zapisane, a idempotencyKey nie jest zużywany
1falsesam plik, z jego typem mediów
1trueZIP z jednym wpisem
≥ 2dowolnieZIP (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

Nazwa pobieranego pliku (options.filename lub nazwa samego pliku) jest sanityzowana: separatory ścieżek, cudzysłowy i znaki zarezerwowane zamieniane są na _, a znaki sterujące, nadpisujące kierunek tekstu, o zerowej szerokości i separatory wierszy są usuwane, więc nazwa nie może udawać pliku innego typu.

Błędy są zawsze w formacie JSON, nigdy binarne: 400 nieprawidłowe żądanie, 401 brak uwierzytelnienia, 403 brak uprawnień, 404 nieznana funkcja lub brak wierszy, 413 odpowiedź przekracza FILE_MAX_BYTES / FILE_MAX_ENTRIES, 500 funkcja zawiodła lub zwróciła nieprawidłową strukturę.

Bezpieczne z założenia

Baza danych kontroluje wyłącznie nazwy plików, zawartość i typy mediów — nigdy nagłówki HTTP. Odpowiedzi zawsze mają Content-Disposition: attachment wraz z X-Content-Type-Options: nosniff, Content-Security-Policy: sandbox i Cache-Control: no-store, więc funkcja zwracająca text/html lub SVG nie może uruchomić skryptu w origin API. Nazwy plików są sanityzowane. Cały wynik jest buforowany w pamięci: rzeczywista granica na żądanie to FILE_MAX_BYTES plus największy pojedynczy wiersz, ponieważ sterownik bazy danych czyta wiersz w całości, zanim limit może zostać sprawdzony — miej na uwadze liczbę równoczesnych pobrań. Sam ZIP jest przesyłany strumieniowo.

Wykrywanie

Funkcja zwracająca zbiór, zadeklarowana z RETURNS TABLE (…) lub parametrami OUT, które zawierają kolumny path i content, jest raportowana przez capabilities z "kind": "file" i endpointem /file. Specyfikacja OpenAPI dokumentuje prawdziwą operację POST /{prefix}/{database}/file (tylko dla ról, które mogą wykonać funkcję plikową); funkcje plikowe nie są udostępniane jako narzędzia MCP.