NextJS Edge Runtime has made handling file uploading quite simple using the FormData interface. We can use FormData in the frontend to send upload request and also use this in server side to process uploads. No external modules are required to handle uploads.
Note that this will work only for App router and not for the Pages router.
Route Structure
We will handle file uploading using the API route handler. For this example, assume a POST request holding the uploaded data is sent at the route /api.
app/
|-- api/
| |-- route.js
uploads/
Server side actions can also be used to handle file uploads on the server side.
Frontend
The frontend will be the usual — a form containing a file input and a submit button. The form when submitted will send a fetch request.
'use client'
export default function FileUpload() {
async function uploadFile(e) {
e.preventDefault()
const file = document.querySelector("[name='file']").files?.[0]
// if no file
if (!file) {
return
}
const body = new FormData()
body.append('file', file)
const response = await fetch('/api', {
method: 'POST',
credentials: 'include',
body
})
let error = null
if (response.status === 200) {
error = ''
} else if (response.status === 400) {
const jsonResponse = await response.json()
error = jsonResponse.message
} else {
error = 'Request Failed'
}
if (error) {
alert(error)
} else {
alert('Success')
}
}
return (
<form onSubmit={uploadFile}>
<input type="file" name="file"/>
<button type="submit">Upload</button>
</form>
)
}
Server Side
On the server side, uploaded file can be fetched and validated using the FormData object.
import { NextResponse } from 'next/server'
import path from 'path'
import { writeFile } from 'fs/promises'
export async function POST(request) {
try {
const formData = await request.formData()
const file = formData.get('file')
// validate for type
if (!['image/jpeg', 'image/png'].includes(file.type)) {
throw new Error('Error: Only JPEG & PNG allowed')
}
// validate for size
if (file.size > 1 * 1024 * 1024) {
throw new Error('Error: Max 1 MB size allowed')
}
// save file
const buffer = Buffer.from(await file.arrayBuffer())
await writeFile(path.join(process.cwd(), `uploads/${file.name}`), buffer)
return NextResponse.json({ error: 0 }, { status: 200 })
} catch (error) {
return NextResponse.json({ error: 1, message: error.message }, { status: 400 })
}
}