Upload Queue

The UploadQueue class manages multiple concurrent file uploads with progress tracking, error handling, and retry capabilities.

Overview

Upload Queue provides:

  • Concurrent Uploads: Upload multiple files simultaneously
  • Progress Tracking: Monitor progress for each file
  • Error Handling: Automatic error detection and retry support
  • Cancellation: Cancel individual or all uploads
  • Queue Management: Add, remove, and clear uploads

Basic Usage

import { DropperClient, UploadQueue } from '@dropper/core'

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

const queue = new UploadQueue(client)

await queue.addFiles(files, {
  maxConcurrent: 3,
  onProgress: (item) => {
    console.log(`${item.file.name}: ${item.progress}%`)
  },
  onSuccess: (item, result) => {
    console.log(`Uploaded: ${result.url}`)
  },
  onError: (item, error) => {
    console.error(`Failed: ${item.file.name}`, error)
  },
})

UploadQueue Class

Constructor

const queue = new UploadQueue(client: DropperClient)

Creates a new upload queue instance with the provided Dropper client.

Methods

addFiles()

Add files to the upload queue and start processing:

await queue.addFiles(
  files: File[],
  options: UploadQueueOptions
): Promise<void>

cancel()

Cancel a specific upload:

queue.cancel(fileId: string): void

retry()

Retry a failed upload:

await queue.retry(
  fileId: string,
  options: UploadQueueOptions
): Promise<void>

getItem()

Get a specific queue item:

const item = queue.getItem(fileId: string): UploadQueueItem | undefined

getAllItems()

Get all queue items:

const items = queue.getAllItems(): UploadQueueItem[]

clearCompleted()

Remove completed uploads from queue:

queue.clearCompleted(): void

clearAll()

Clear all uploads and cancel active ones:

queue.clearAll(): void

UploadQueueOptions

interface UploadQueueOptions {
  folderId?: string                      // Upload to specific folder
  metadata?: Record<string, unknown>     // Custom metadata for all files
  maxConcurrent?: number                 // Max concurrent uploads (default: 3)
  onProgress?: (item: UploadQueueItem) => void    // Progress callback
  onSuccess?: (item: UploadQueueItem, result: FileResponseDto) => void  // Success callback
  onError?: (item: UploadQueueItem, error: Error) => void  // Error callback
}

UploadQueueItem

Each file in the queue is represented by an UploadQueueItem:

interface UploadQueueItem {
  id: string                                    // Unique item ID
  file: File                                    // File object
  status: 'pending' | 'uploading' | 'success' | 'error'  // Upload status
  progress: number                              // Progress (0-100)
  error?: Error                                 // Error if failed
  result?: FileResponseDto                      // Result if successful
}

Complete Example

import { DropperClient, UploadQueue } from '@dropper/core'

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

const queue = new UploadQueue(client)

async function uploadMultipleFiles(files: File[]) {
  await queue.addFiles(files, {
    maxConcurrent: 3,
    folderId: 'folder_123',
    metadata: {
      uploadedBy: 'user_456',
      batchId: 'batch_789',
    },
    onProgress: (item) => {
      console.log(`${item.file.name}: ${item.progress}%`)
      updateProgressBar(item.id, item.progress)
    },
    onSuccess: (item, result) => {
      console.log(`✓ ${item.file.name} uploaded successfully`)
      console.log(`  URL: ${result.url}`)
      showSuccessNotification(item.file.name)
    },
    onError: (item, error) => {
      console.error(`✗ ${item.file.name} failed:`, error.message)
      showErrorNotification(item.file.name, error.message)
    },
  })
  
  console.log('All uploads completed!')
}

Progress Tracking

Track progress for all files:

const queue = new UploadQueue(client)

await queue.addFiles(files, {
  maxConcurrent: 3,
  onProgress: (item) => {
    // Update individual file progress
    updateFileProgress(item.id, item.progress)
    
    // Calculate overall progress
    const allItems = queue.getAllItems()
    const totalProgress = allItems.reduce((sum, item) => sum + item.progress, 0)
    const overallProgress = totalProgress / allItems.length
    
    updateOverallProgress(overallProgress)
  },
})

Error Handling

Handle errors and retry failed uploads:

const queue = new UploadQueue(client)

await queue.addFiles(files, {
  maxConcurrent: 3,
  onError: (item, error) => {
    console.error(`Upload failed: ${item.file.name}`)
    console.error(`Error: ${error.message}`)
    
    // Show retry button
    showRetryButton(item.id)
  },
})

// Retry failed upload
async function retryUpload(fileId: string) {
  await queue.retry(fileId, {
    maxConcurrent: 3,
    onProgress: (item) => {
      console.log(`Retrying ${item.file.name}: ${item.progress}%`)
    },
    onSuccess: (item, result) => {
      console.log(`Retry successful: ${result.url}`)
    },
    onError: (item, error) => {
      console.error(`Retry failed: ${error.message}`)
    },
  })
}

Cancel Uploads

Cancel individual or all uploads:

const queue = new UploadQueue(client)

// Start uploads
await queue.addFiles(files, {
  maxConcurrent: 3,
  onProgress: (item) => {
    console.log(`${item.file.name}: ${item.progress}%`)
  },
})

// Cancel specific upload
queue.cancel('file-id-123')

// Cancel all uploads
queue.clearAll()

Queue Management

Manage queue items:

const queue = new UploadQueue(client)

// Get all items
const allItems = queue.getAllItems()
console.log(`Total files: ${allItems.length}`)

// Get specific item
const item = queue.getItem('file-id-123')
if (item) {
  console.log(`Status: ${item.status}`)
  console.log(`Progress: ${item.progress}%`)
}

// Clear completed uploads
queue.clearCompleted()

// Get remaining items
const remaining = queue.getAllItems()
console.log(`Remaining: ${remaining.length}`)

Status Monitoring

Monitor upload status:

const queue = new UploadQueue(client)

await queue.addFiles(files, {
  maxConcurrent: 3,
  onProgress: (item) => {
    const allItems = queue.getAllItems()
    
    const pending = allItems.filter(i => i.status === 'pending').length
    const uploading = allItems.filter(i => i.status === 'uploading').length
    const success = allItems.filter(i => i.status === 'success').length
    const error = allItems.filter(i => i.status === 'error').length
    
    console.log(`Status: ${pending} pending, ${uploading} uploading, ${success} success, ${error} error`)
  },
})

React Integration Example

import { useState, useEffect } from 'react'
import { DropperClient, UploadQueue } from '@dropper/core'

function FileUploader() {
  const [queue] = useState(() => new UploadQueue(client))
  const [items, setItems] = useState<UploadQueueItem[]>([])
  
  const handleUpload = async (files: File[]) => {
    await queue.addFiles(files, {
      maxConcurrent: 3,
      onProgress: (item) => {
        setItems(queue.getAllItems())
      },
      onSuccess: (item, result) => {
        setItems(queue.getAllItems())
        console.log('Uploaded:', result.url)
      },
      onError: (item, error) => {
        setItems(queue.getAllItems())
        console.error('Failed:', error)
      },
    })
  }
  
  return (
    <div>
      <input
        type="file"
        multiple
        onChange={(e) => handleUpload(Array.from(e.target.files || []))}
      />
      
      <div>
        {items.map((item) => (
          <div key={item.id}>
            <span>{item.file.name}</span>
            <span>{item.status}</span>
            <progress value={item.progress} max={100} />
            
            {item.status === 'error' && (
              <button onClick={() => queue.retry(item.id, { maxConcurrent: 3 })}>
                Retry
              </button>
            )}
            
            {item.status === 'uploading' && (
              <button onClick={() => queue.cancel(item.id)}>
                Cancel
              </button>
            )}
          </div>
        ))}
      </div>
    </div>
  )
}

Best Practices

1. Set Appropriate Concurrency

Balance speed and resource usage:

// For fast connections
await queue.addFiles(files, { maxConcurrent: 5 })

// For slower connections or large files
await queue.addFiles(files, { maxConcurrent: 2 })

2. Provide Visual Feedback

Show progress for each file and overall progress:

await queue.addFiles(files, {
  maxConcurrent: 3,
  onProgress: (item) => {
    // Individual progress
    updateFileProgress(item.id, item.progress)
    
    // Overall progress
    const allItems = queue.getAllItems()
    const overall = allItems.reduce((sum, i) => sum + i.progress, 0) / allItems.length
    updateOverallProgress(overall)
  },
})

3. Handle Errors Gracefully

Provide retry options for failed uploads:

await queue.addFiles(files, {
  maxConcurrent: 3,
  onError: (item, error) => {
    showErrorMessage(`${item.file.name} failed: ${error.message}`)
    showRetryButton(item.id)
  },
})

4. Clean Up Completed Uploads

Remove completed uploads to free memory:

await queue.addFiles(files, {
  maxConcurrent: 3,
  onSuccess: (item) => {
    // Wait a bit before clearing
    setTimeout(() => {
      queue.clearCompleted()
    }, 2000)
  },
})

Next Steps