Quickstart Guide
This guide will walk you through creating a complete file upload system with Nuxt File Storage. You’ll build a simple page that allows users to select files and upload them to your server.
Complete Example
Follow these steps to implement file uploads in your Nuxt application:
Create the Frontend Component
Create a Vue component with a file input and upload button. Use the useFileStorage composable to handle file selection.<template>
<div>
<h1>Upload Files</h1>
<input
type="file"
multiple
@input="handleFileInput"
/>
<button @click="submit">Upload</button>
<p v-if="uploadStatus">{{ uploadStatus }}</p>
<!-- Preview selected files -->
<div v-if="files.length > 0">
<h2>Selected Files:</h2>
<div v-for="file in files" :key="file.name">
<img
v-if="file.type.startsWith('image/')"
:src="file.content"
:alt="file.name"
style="max-width: 200px; margin: 10px;"
/>
<p>{{ file.name }} ({{ (file.size / 1024).toFixed(2) }} KB)</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
// Initialize the file storage composable
// clearOldFiles: true (default) clears previous files when new ones are selected
const { handleFileInput, files } = useFileStorage({ clearOldFiles: true })
const uploadStatus = ref('')
const submit = async () => {
try {
const response = await $fetch('/api/files', {
method: 'POST',
body: {
files: files.value,
},
})
uploadStatus.value = `Successfully uploaded ${response.length} file(s)!`
} catch (error) {
uploadStatus.value = 'Upload failed. Please try again.'
}
}
</script>
The handleFileInput function automatically serializes files to base64 data URLs with metadata. The files ref contains an array of ClientFile objects ready to send to your backend.
Create the API Route
Create a server API route to receive and store the uploaded files.import { ServerFile } from "#file-storage/types"
export default defineEventHandler(async (event) => {
// Read the uploaded files from the request body
const { files } = await readBody<{ files: ServerFile[] }>(event)
const fileNames: string[] = []
// Store each file
for (const file of files) {
// Store the file with a custom name in a specific folder
const fileName = await storeFileLocally(
file, // The file object
8, // Generate a random 8-character ID as filename
'/uploads' // Store in the 'uploads' folder within your mount point
)
fileNames.push(fileName)
}
// Return the stored file names
return fileNames
})
The second parameter of storeFileLocally can be either:
- A number to generate a random ID of that length (e.g.,
8 → aB3dE7fG.jpg)
- A string to use as the filename (e.g.,
'profile-picture' → profile-picture.jpg)
The file extension is automatically added based on the file’s MIME type. Configure Storage Location
Make sure you’ve configured the storage mount point in your nuxt.config.ts:export default defineNuxtConfig({
modules: ['nuxt-file-storage'],
fileStorage: {
mount: process.env.FILE_STORAGE_MOUNT || './server/files',
},
})
Files will be stored at: {mount}/uploads/{filename} Test Your Implementation
- Start your development server:
npm run dev
- Navigate to your page
- Select one or more files
- Click the upload button
- Files will be stored in your configured directory!
Understanding the Flow
Here’s what happens when a user uploads a file:
- User selects files - The
<input type="file"> triggers the @input event
- Serialization -
handleFileInput reads each file and converts it to a base64 data URL
- Client state - The
files ref is updated with ClientFile objects containing:
name - Original filename
content - Base64 data URL
size - File size in bytes
type - MIME type
lastModified - Last modified timestamp
- Transmission - Files are sent to the API route via
$fetch
- Backend processing - The API route receives
ServerFile objects (same structure as ClientFile)
- Storage -
storeFileLocally decodes the base64 content and writes it to disk
- Path validation - Built-in security checks prevent path traversal attacks
If you have multiple file input fields on the same page, create a separate instance of useFileStorage for each:
<template>
<div>
<!-- Profile picture input -->
<input type="file" @input="handleProfileInput" />
<!-- Document upload input -->
<input type="file" multiple @input="handleDocumentInput" />
</div>
</template>
<script setup lang="ts">
// Separate instance for profile picture
const {
handleFileInput: handleProfileInput,
files: profileFiles
} = useFileStorage()
// Separate instance for documents
const {
handleFileInput: handleDocumentInput,
files: documentFiles
} = useFileStorage()
</script>
Advanced Storage Options
You can customize how files are stored:
import { ServerFile } from "#file-storage/types"
export default defineEventHandler(async (event) => {
const { files } = await readBody<{ files: ServerFile[] }>(event)
for (const file of files) {
// Option 1: Use a specific filename
await storeFileLocally(file, 'profile-avatar', '/users/avatars')
// Result: {mount}/users/avatars/profile-avatar.jpg
// Option 2: Generate a random ID
await storeFileLocally(file, 12, '/documents')
// Result: {mount}/documents/aB3dE7fG9hIj.pdf
// Option 3: Parse the data URL manually
const { binaryString, ext } = parseDataUrl(file.content)
// Use binaryString and ext for custom processing
}
})
All file paths are validated to prevent directory traversal attacks. Attempting to use paths like ../ will throw an error.
Next Steps
Now that you have file uploads working, explore more features: