File Upload

Upload files to Dropper with progress tracking, metadata, and folder organization.

Basic Upload

Upload a single file:

import { DropperClient } from '@dropper/core'

const client = new DropperClient({
  publishableKey: 'pk_dropper_test_xxx',
})

const file = new File(['content'], 'example.txt')
const result = await client.uploadFile(file)

console.log('Uploaded:', result.url)

Upload with Progress

Track upload progress with a callback:

const result = await client.uploadFile(file, {
  onProgress: (progress) => {
    console.log(`Upload progress: ${progress}%`)
    // Update UI progress bar
    updateProgressBar(progress)
  },
})

Upload Options

UploadOptions Interface

interface UploadOptions {
  folderId?: string                      // Upload to specific folder
  metadata?: Record<string, unknown>     // Custom metadata
  onProgress?: (progress: number) => void // Progress callback (0-100)
  signal?: AbortSignal                   // Abort controller signal
}

Upload to Folder

Organize files by uploading to a specific folder:

const result = await client.uploadFile(file, {
  folderId: 'folder_abc123',
})

Add Custom Metadata

Attach custom metadata to files:

const result = await client.uploadFile(file, {
  metadata: {
    userId: '123',
    category: 'profile-pictures',
    tags: ['avatar', 'user'],
  },
})

Upload Process

Dropper uses a 3-step upload process for optimal performance:

Step 1: Get Signed URL

The client requests a signed upload URL from the Dropper API:

// Internal: Client sends file metadata
POST /files/sdk/upload-url
{
  "filename": "photo.jpg",
  "mimeType": "image/jpeg",
  "size": 1024000,
  "folderId": "folder_123",
  "metadata": { "userId": "123" }
}

// Response: Signed URL for direct upload
{
  "fileId": "file_abc123",
  "uploadUrl": "https://storage.provider.com/...",
  "uploadMethod": "PUT",
  "fileKey": "uploads/file_abc123"
}

Step 2: Direct Upload

The file is uploaded directly to the storage provider (S3, Azure, etc.):

// Internal: Direct PUT to storage provider
PUT https://storage.provider.com/...
Content-Type: image/jpeg
Body: [file binary data]

This direct upload:

  • Bypasses your server
  • Reduces bandwidth costs
  • Improves upload speed
  • Provides progress tracking

Step 3: Confirm Upload

The client confirms the upload completion:

// Internal: Confirm upload
POST /files/sdk/{fileId}/confirm
{
  "metadata": { "userId": "123" }
}

// Response: Complete file record
{
  "id": "file_abc123",
  "originalFilename": "photo.jpg",
  "url": "https://cdn.dropper.com/...",
  ...
}

Cancel Upload

Cancel an in-progress upload using AbortController:

const abortController = new AbortController()

// Start upload
const uploadPromise = client.uploadFile(file, {
  signal: abortController.signal,
  onProgress: (progress) => console.log(`${progress}%`),
})

// Cancel after 2 seconds
setTimeout(() => {
  abortController.abort()
  console.log('Upload cancelled')
}, 2000)

try {
  await uploadPromise
} catch (error) {
  console.error('Upload cancelled or failed:', error)
}

File Response

After successful upload, you receive a FileResponseDto:

interface FileResponseDto {
  id: string                    // Unique file ID
  originalFilename: string      // Original filename
  mimeType: string             // MIME type (e.g., "image/jpeg")
  size: number                 // File size in bytes
  storagePath: string          // Storage path
  isPublic: boolean            // Public access flag
  extension?: string           // File extension (e.g., "jpg")
  virtualPath?: string         // Virtual path
  width?: number               // Image width (if image)
  height?: number              // Image height (if image)
  duration?: number            // Video duration (if video)
  url: string                  // Public URL
  thumbnailUrl?: string        // Thumbnail URL (if image/video)
  folderId?: string            // Parent folder ID
  appId: string                // App ID
  metadata?: Record<string, unknown> // Custom metadata
  createdAt: string            // ISO timestamp
  updatedAt: string            // ISO timestamp
}

Complete Example

import { DropperClient } from '@dropper/core'

const client = new DropperClient({
  publishableKey: process.env.DROPPER_KEY!,
})

async function uploadWithProgress(file: File) {
  const abortController = new AbortController()
  
  try {
    const result = await client.uploadFile(file, {
      folderId: 'folder_123',
      metadata: {
        uploadedBy: 'user_456',
        category: 'documents',
      },
      onProgress: (progress) => {
        console.log(`Uploading ${file.name}: ${progress}%`)
        
        // Update UI
        updateProgressBar(progress)
        
        // Cancel if user clicks cancel button
        if (shouldCancel()) {
          abortController.abort()
        }
      },
      signal: abortController.signal,
    })
    
    console.log('Upload successful!')
    console.log('File URL:', result.url)
    console.log('File ID:', result.id)
    console.log('File size:', result.size, 'bytes')
    
    // Image-specific info
    if (result.width && result.height) {
      console.log(`Dimensions: ${result.width}x${result.height}`)
    }
    
    return result
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Upload cancelled by user')
    } else {
      console.error('Upload failed:', error)
    }
    throw error
  }
}

Error Handling

Handle upload errors gracefully:

try {
  const result = await client.uploadFile(file)
} catch (error) {
  if (error.statusCode === 401) {
    console.error('Invalid API key')
  } else if (error.statusCode === 413) {
    console.error('File too large')
  } else if (error.statusCode === 415) {
    console.error('File type not supported')
  } else {
    console.error('Upload failed:', error.message)
  }
}

Best Practices

1. Validate Before Upload

Validate files before uploading to provide immediate feedback:

import { validateFiles } from '@dropper/core'

const errors = validateFiles([file], {
  accept: 'image/*',
  maxSize: 5 * 1024 * 1024, // 5MB
})

if (errors.length > 0) {
  console.error('Validation failed:', errors[0].message)
  return
}

// Proceed with upload
const result = await client.uploadFile(file)

2. Show Progress Feedback

Always show upload progress to users:

const result = await client.uploadFile(file, {
  onProgress: (progress) => {
    // Update progress bar
    progressBar.style.width = `${progress}%`
    progressText.textContent = `${progress}%`
  },
})

3. Handle Errors

Provide clear error messages:

try {
  const result = await client.uploadFile(file)
  showSuccess('File uploaded successfully!')
} catch (error) {
  showError(`Upload failed: ${error.message}`)
}

4. Use Metadata

Add metadata for better file organization:

const result = await client.uploadFile(file, {
  metadata: {
    userId: currentUser.id,
    uploadDate: new Date().toISOString(),
    source: 'web-app',
  },
})

Next Steps