CORE CONCEPTS

fileUpload() Middleware

fileUpload() provides Multer-style multipart handling on top of the Web Standard request.formData() API. If you've used Multer with Express, the API will feel immediately familiar.

Basic Usage

Single file upload
import { fileUpload } from '@rasenganjs/futon'; const upload = fileUpload({ limits: { fileSize: 5 * 1024 * 1024 } }); // 5 MB router.post('/avatar', upload.single('avatar'), async (ctx) => { const file = ctx.get('file'); // UploadedFile return json({ size: file.size, name: file.originalname }); });

The Uploader Methods

fileUpload(options) returns an Uploader with five methods, each producing a Middleware:

MethodAcceptsPopulates
.single(field)One file on fieldctx.state.file: UploadedFile
.array(field, maxCount?)Up to maxCount files on fieldctx.state.files: UploadedFile[]
.fields(specs)The declared fieldsctx.state.files: Record<string, UploadedFile[]>
.none()Text fields only, any file is rejectedNone
.any()Every file, regardless of field namectx.state.files: UploadedFile[]
Multiple files on named fields
const upload = fileUpload(); router.post( '/gallery', upload.fields([ { name: 'cover', maxCount: 1 }, { name: 'photos', maxCount: 10 }, ]), async (ctx) => { const { cover, photos } = ctx.get('files'); return json({ cover: cover?.[0]?.originalname, count: photos?.length }); } );

Non-file fields in the same multipart body become ctx.body, just like Multer's req.body.

UploadOptions

OptionDescription
storageStorage engine, defaults to MemoryStorage (see Storage Engines)
limits.fileSizeMaximum size of a single file, in bytes
limits.filesMaximum number of files in the whole request
limits.fieldsMaximum number of non-file text fields
fileFilter(ctx, info) => boolean | Promise<boolean>, reject files before they reach storage
Rejecting non-image uploads
const upload = fileUpload({ fileFilter: (ctx, info) => info.mimetype.startsWith('image/'), });

Unlike Multer, a rejected file produces an explicit 400 FILE_FILTER_REJECTED response rather than silently skipping the file.

Error Responses

Every rejection returns JSON with an error code you can match on:

Example error response
{ "error": { "code": "LIMIT_FILE_SIZE", "message": "File \"photo.png\" exceeds the 5242880 byte limit." } }
CodeCause
LIMIT_FILE_SIZEA file exceeded limits.fileSize
LIMIT_FILE_COUNTToo many files in the request
LIMIT_FIELD_COUNTToo many non-file fields
LIMIT_UNEXPECTED_FILEA file arrived on a field the selection doesn't allow
FILE_FILTER_REJECTEDfileFilter returned false
MALFORMED_BODYThe multipart body couldn't be parsed

Validation Order and Rollback

Every part of the multipart body is validated before anything is stored, limits, unexpected fields, and fileFilter are all checked first. If a file fails to store partway through a multi-file batch, already-stored files are rolled back via the storage engine's removeFile().

Custom Middleware
Storage Engines