File Storage

Upload handling, S3 compatible storage, and image optimization

File Storage

Handle file uploads with S3-compatible storage providers.

Supported Providers

  • Cloudflare R2 (recommended)
  • AWS S3
  • DigitalOcean Spaces
  • MinIO
  • Backblaze B2

Configuration

Environment Variables

# Cloudflare R2
R2_ACCOUNT_ID=your-account-id
R2_ACCESS_KEY_ID=your-access-key
R2_SECRET_ACCESS_KEY=your-secret-key
R2_BUCKET_NAME=your-bucket

# Or AWS S3
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=your-access-key
S3_SECRET_ACCESS_KEY=your-secret-key
S3_BUCKET_NAME=your-bucket

File Upload

Client-Side Upload

<script setup>
const file = ref(null)

async function uploadFile() {
  const formData = new FormData()
  formData.append('file', file.value)
  
  const response = await $fetch('/api/upload', {
    method: 'POST',
    body: formData
  })
  
  console.log('File uploaded:', response.url)
}
</script>

<template>
  <input type="file" @change="file = $event.target.files[0]">
  <button @click="uploadFile">Upload</button>
</template>

Server-Side Handler

// server/api/upload.post.ts
export default defineEventHandler(async (event) => {
  const form = await readMultipartFormData(event)
  const file = form.find(item => item.name === 'file')
  
  if (!file) {
    throw createError({
      statusCode: 400,
      message: 'No file provided'
    })
  }
  
  // Upload to storage
  const url = await uploadFile(file)
  
  return { url }
})

Image Optimization

// Resize and optimize image
import sharp from 'sharp'

const optimized = await sharp(file.data)
  .resize(800, 600, { fit: 'inside' })
  .jpeg({ quality: 80 })
  .toBuffer()

await uploadFile(optimized, 'optimized-image.jpg')

Direct Upload (Pre-signed URLs)

// Generate pre-signed URL
const { uploadUrl, downloadUrl } = await $fetch('/api/upload/presigned', {
  method: 'POST',
  body: {
    filename: 'document.pdf',
    contentType: 'application/pdf'
  }
})

// Upload directly to storage
await fetch(uploadUrl, {
  method: 'PUT',
  body: file,
  headers: {
    'Content-Type': 'application/pdf'
  }
})

File Types

Allowed File Types

Configure allowed file types:

const allowedTypes = [
  'image/jpeg',
  'image/png',
  'image/gif',
  'image/webp',
  'application/pdf',
  'application/msword',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
]

function validateFileType(file: File) {
  return allowedTypes.includes(file.type)
}

File Size Limits

const MAX_FILE_SIZE = 10 * 1024 * 1024 // 10MB

function validateFileSize(file: File) {
  return file.size <= MAX_FILE_SIZE
}

Security Best Practices

  1. Validate file types and sizes
  2. Scan files for malware
  3. Use unique filenames to prevent overwriting
  4. Set appropriate CORS policies
  5. Use CDN for public files
  6. Implement rate limiting

Next Steps