Passa al contenuto principale

Upload di file

Sullo stack Express, Efesto collega Multer alla catena dei middleware così che un endpoint possa accettare multipart/form-data. Aderisci per metodo con un flag booleano; Efesto gestisce il resto ed espone il/i file sulla request.

Entrambi gli stack

I flag _<verb>Multer / _<verb>MultipleMulter funzionano su entrambi gli stack. Su Express, Efesto collega Multer e il file arriva in req.file / req.files. Su Bun/Elysia, Efesto imposta lo schema del corpo TypeBox a t.File() / t.Files() e il file arriva in ctx.body (vedi sotto). Puoi anche accettare file nativamente su Elysia con un tuo schema del corpo t.File().

File singolo

Servono due cose: documentare il corpo come multipart/form-data e impostare il flag _<method>Multer a true. Il file caricato è poi disponibile come req.file:

import { BaseApiService, SwaggerOptions } from "efesto";
import type { Request, Response } from "express";

class Documents extends BaseApiService {
constructor() {
super(__filename);
}

_postSwagger: SwaggerOptions = {
operationId: "uploadDocument",
requestBody: {
content: {
"multipart/form-data": {
schema: {
type: "object",
properties: {
file: { type: "string", format: "binary" },
},
},
},
},
},
};

// abilita Multer per POST (file singolo)
_postMulter: boolean = true;

_post(req: Request, res: Response) {
const file = req.file; // il file caricato (in memoria)
return res.json({ name: file?.originalname, size: file?.size });
}
}
export default Documents;

Il nome del campo che Multer si aspetta per un upload singolo è file.

File multipli

Aggiungi _<method>MultipleMulter = true accanto al flag singolo, documenta il corpo come array di binari e leggi req.files:

class Documents extends BaseApiService {
constructor() { super(__filename); }

_postSwagger: SwaggerOptions = {
operationId: "uploadDocuments",
requestBody: {
content: {
"multipart/form-data": {
schema: {
type: "object",
properties: {
files: {
type: "array",
items: { type: "string", format: "binary" },
},
},
},
},
},
},
};

_postMulter: boolean = true;
_postMultipleMulter: boolean = true; // richiesto per file multipli

_post(req: Request, res: Response) {
const files = req.files; // array di file caricati
return res.json({ count: Array.isArray(files) ? files.length : 0 });
}
}

Il nome del campo per upload multipli è files.

Come si comporta

  • Efesto usa di default lo storage in memoria di Multer, quindi i file arrivano come buffer (req.file.buffer / file.buffer): nulla viene scritto su disco per te.
  • Il middleware Multer viene eseguito per primo nella catena, prima della validazione e del tuo handler, così req.file / req.files sono popolati quando il metodo viene eseguito.
  • I flag sono letti per nome del metodo: _postMulter, _putMulter, ecc., corrispondenti al verbo HTTP dell'endpoint.

Sullo stack Bun/Elysia

Gli stessi flag si applicano a una classe che estende BaseApiService. Invece di collegare Multer, Efesto imposta lo schema del corpo TypeBox della rotta (t.File() per _<verb>Multer, t.Files() per _<verb>MultipleMulter) quando non ne dichiari uno tu stesso; il file arriva in ctx.body come un File web:

import { BaseApiService, type Context, SwaggerOptions } from "efesto/elysia";

export default class extends BaseApiService {
_putSwagger: SwaggerOptions = {
operationId: "uploadFile",
requestBody: {
content: {
"multipart/form-data": {
schema: { type: "object", properties: { file: "string::binary" } },
},
},
},
};

_putMulter = true; // lo schema del corpo diventa t.Object({ file: t.File() })

async _put({ body }: Context) {
const file = (body as { file: File }).file;
return file?.name;
}
}

I nomi dei campi corrispondono a Express: file per un upload singolo, files per multipli. I flag esistono solo su post / patch / put.

Validare e memorizzare i file

Efesto porta i byte al tuo handler; cosa fai dopo è il tuo codice. Valida tipo e dimensione, poi salva dove vuoi, tutto all'interno del metodo:

_post(req: Request, res: Response) {
const file = req.file;
if (!file) return res.status(400).json({ error: "No file" });
if (file.size > 5 * 1024 * 1024) return res.status(413).json({ error: "Too large" });
if (!file.mimetype.startsWith("image/")) return res.status(415).json({ error: "Images only" });

// memorizza file.buffer dove preferisci: disco, S3, GCS, un database...
const id = persist(file.buffer, file.originalname); // la tua logica di storage
return res.json({ id });
}
Fuori ambito

Lo storage su cloud (S3, GCS), l'elaborazione delle immagini, la compressione e la scansione antivirus non sono funzionalità di Efesto. Sono codice ordinario che esegui su file.buffer usando la libreria che preferisci.