Download di file (/file)
Download di file
L’endpoint /file serve file binari generati da una funzione PostgreSQL — senza base64 dentro JSON-RPC. Un singolo file restituito viene inviato così com’è; più file vengono impacchettati al volo in un archivio ZIP.
POST /{prefisso}/{database}/fileAutenticazione, cambio di ruolo (SET LOCAL ROLE), rate limiting e limite della dimensione della richiesta sono identici a /jsonrpc. Il chiamante deve avere EXECUTE sulla funzione.
Richiesta
{
"method": "api.export_documents",
"params": { "count": 3 },
"options": { "filename": "export.zip", "force_zip": false, "compression_level": 6 },
"idempotencyKey": "optional"
}| Campo | Descrizione |
|---|---|
method | Obbligatorio. schema.function, chiamata come fn(params::jsonb). |
params | Passato alla funzione come jsonb (predefinito {}). |
options.filename | Nome del file/ZIP scaricato. Predefinito: il nome del file stesso, oppure export-YYYYMMDD-HHMMSS.zip. |
options.force_zip | Restituisce sempre uno ZIP, anche per un singolo file. |
options.compression_level | 0 = solo archiviazione, 1–9; predefinito 6. |
Contratto con il database
La funzione accetta un argomento jsonb e restituisce un insieme di righe. path e content sono obbligatori; mime_type e store_only sono opzionali.
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;
$$;pathdeve essere un semplice percorso relativo che sia sicuro da estrarre su qualsiasi sistema operativo. Un percorso viene rifiutato (la richiesta fallisce con 500 e il problema viene registrato nel log) se:- è vuoto, più lungo di 512 byte, non è UTF-8 valido o contiene caratteri di controllo;
- è assoluto (
/…,C:…) o contiene un backslash; - ha un segmento vuoto,
.o..; - ha un segmento che termina con un punto o uno spazio (Windows li rimuove, quindi
".. "diventerebbe..); - contiene
:(alternate data stream di NTFS) o uno tra< > " | ? *; - usa come segmento un nome di dispositivo riservato di Windows, con o senza estensione (
CON,PRN,AUX,NUL,COM0–COM9,LPT0–LPT9); - collide con un altro percorso nella stessa risposta: i nomi vengono confrontati senza distinzione tra maiuscole e minuscole (
a.txteA.TXTentrano in conflitto su macOS/Windows) e un file non può essere anche una directory (aea/b).
contentNULLè un file vuoto.mime_typeviene usato solo se è un media type ben formato, altrimenti viene dedotto dall’estensione (fallbackapplication/octet-stream).store_only = truememorizza la voce ZIP senza compressione (necessario ad es. per il filemimetypedi EPUB).
Risposta
| Righe | force_zip | Risultato |
|---|---|---|
| 0 | qualsiasi | 404 (errore JSON); la transazione viene annullata (rollback), quindi nulla di ciò che la funzione ha fatto viene mantenuto e un idempotencyKey non viene consumato |
| 1 | false | il file stesso, con il suo media type |
| 1 | true | ZIP con una voce |
| ≥ 2 | qualsiasi | 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}}' -OJIl nome di download (options.filename o il nome del file stesso) viene sanificato: i separatori di percorso, le virgolette
e i caratteri riservati diventano _, e i caratteri di controllo, di override bidirezionale, a larghezza zero
e i separatori di riga vengono rimossi, così un nome non può essere camuffato da un altro tipo di file.
Gli errori sono sempre JSON, mai binari: 400 richiesta non valida, 401 non autenticato, 403 nessun permesso, 404 funzione sconosciuta o nessuna riga, 413 la risposta supera FILE_MAX_BYTES / FILE_MAX_ENTRIES, 500 funzione fallita o struttura restituita non valida.
Sicuro per costruzione
Il database controlla solo i nomi dei file, i contenuti e i media type — mai gli header HTTP. Le risposte hanno sempre Content-Disposition: attachment con X-Content-Type-Options: nosniff, Content-Security-Policy: sandbox e Cache-Control: no-store, quindi una funzione che restituisce text/html o SVG non può eseguire script nell’origine dell’API. I nomi dei file vengono sanificati. L’intero risultato viene tenuto in memoria: il limite reale per richiesta è FILE_MAX_BYTES più la singola riga più grande, perché il driver del database legge una riga per intero prima che il limite possa essere verificato — tenere presente il numero di download simultanei. Lo ZIP stesso viene trasmesso in streaming.
Discovery
Una funzione set-returning dichiarata con RETURNS TABLE (…) o parametri OUT che
include le colonne path e content viene riportata da capabilities con "kind": "file" e endpoint /file. La specifica OpenAPI documenta una vera operazione POST /{prefisso}/{database}/file (solo per i ruoli che possono eseguire una funzione file); le funzioni file non sono esposte come tool MCP.