form-data-parser
The form-data-parser package provides parseFormData, a drop-in replacement for request.formData() that streams file uploads instead of buffering them in memory. It returns a standard FormData object where file fields are represented as FileUpload instances.
Installation
The form data parser is included with Remix. No additional installation is needed.
Import
import { parseFormData, FileUpload } from 'remix/form-data-parser'API
parseFormData(request, options?, uploadHandler?)
Parses a multipart or URL-encoded request body and returns a FormData object. File fields are represented as FileUpload instances instead of File objects, giving you access to streaming and metadata.
function parseFormData(
request: Request,
options?: ParseFormDataOptions,
uploadHandler?: UploadHandler,
): Promise<FormData>When called without an upload handler, files are buffered in memory:
let formData = await parseFormData(request)
let title = formData.get('title') as string
let file = formData.get('avatar') as FileUpload
console.log(file.name) // 'photo.jpg'
console.log(file.type) // 'image/jpeg'
console.log(file.size) // 1048576When called with an upload handler, files are streamed to the handler:
let formData = await parseFormData(request, {}, async (fileUpload) => {
// Stream the file to disk, S3, etc.
let url = await uploadToS3(fileUpload)
return url
})
let avatarUrl = formData.get('avatar') as stringFileUpload
A class representing an uploaded file. It extends the web File interface with additional metadata.
class FileUpload {
readonly fieldName: string // Form field name
readonly name: string // Original filename
readonly type: string // MIME type
readonly size: number // Size in bytes
arrayBuffer(): Promise<ArrayBuffer>
text(): Promise<string>
stream(): ReadableStream<Uint8Array>
}let file = formData.get('document') as FileUpload
console.log(file.fieldName) // 'document'
console.log(file.name) // 'report.pdf'
console.log(file.type) // 'application/pdf'
console.log(file.size) // 2048576
// Read the file content
let bytes = await file.arrayBuffer()
let text = await file.text()
let stream = file.stream()Upload Handler
An upload handler is a function that receives a FileUpload and returns a value to store in the FormData result. Use upload handlers to stream files to storage instead of buffering them in memory.
type UploadHandler = (fileUpload: FileUpload) => Promise<any>The value returned by the handler replaces the FileUpload in the resulting FormData. If the handler returns undefined, the field is omitted.
Options
| Option | Type | Default | Description |
|---|---|---|---|
maxFileSize | number | Infinity | Maximum size in bytes for a single uploaded file. |
maxTotalSize | number | Infinity | Maximum total size in bytes across all uploaded files. |
maxParts | number | Infinity | Maximum number of form fields (both text and file). |
let formData = await parseFormData(request, {
maxFileSize: 5 * 1024 * 1024, // 5 MB per file
maxTotalSize: 20 * 1024 * 1024, // 20 MB total
maxParts: 20,
})Examples
Basic File Upload
import { parseFormData, FileUpload } from 'remix/form-data-parser'
router.map(uploadRoute, async ({ request }) => {
let formData = await parseFormData(request)
let title = formData.get('title') as string
let cover = formData.get('cover') as FileUpload
console.log(title) // 'My Album'
console.log(cover.name) // 'photo.jpg'
console.log(cover.type) // 'image/jpeg'
return new Response('OK')
})Upload to Filesystem
import { parseFormData } from 'remix/form-data-parser'
import { writeFile } from 'remix/fs'
let formData = await parseFormData(request, {}, async (fileUpload) => {
let path = `./uploads/${fileUpload.name}`
await writeFile(path, fileUpload)
return path
})
let savedPath = formData.get('avatar') as string
// './uploads/photo.jpg'Upload to S3
import { parseFormData } from 'remix/form-data-parser'
import { createS3FileStorage } from 'remix/file-storage-s3'
let s3 = createS3FileStorage({
bucket: 'my-uploads',
region: 'us-east-1',
})
let formData = await parseFormData(request, {}, async (fileUpload) => {
let key = `uploads/${Date.now()}-${fileUpload.name}`
await s3.set(key, fileUpload)
return key
})Multiple Files
let formData = await parseFormData(request)
let photos = formData.getAll('photos') as FileUpload[]
for (let photo of photos) {
console.log(photo.name, photo.size)
}Filtering by Field Name
Only process specific file fields in the upload handler:
let formData = await parseFormData(request, {}, async (fileUpload) => {
if (fileUpload.fieldName === 'avatar') {
return await saveAvatar(fileUpload)
}
// Return undefined to skip other file fields
})Related
- multipart-parser --- Low-level multipart parsing.
- file-storage --- File storage abstraction.
- File Uploads Guide --- End-to-end guide to file uploads in Remix.