another_tus_client 3.2.6 copy "another_tus_client: ^3.2.6" to clipboard
another_tus_client: ^3.2.6 copied to clipboard

A web compatible tus client for dart allowing resumable uploads using the tus protocol.

Another TUS Client #

Pub Version
Platforms

A Dart client for resumable file uploads using the TUS protocol.
Forked from tus_client_dart.

tus is an HTTP‑based protocol for resumable file uploads.
It enables interruption (intentional or accidental) and later resumption of uploads without needing to restart from the beginning.


Table of Contents #

Usage Examples #

1. Creating a Client #

import 'package:another_tus_client/another_tus_client.dart';
import 'package:cross_file/cross_file.dart';

final file = XFile("/path/to/my/pic.jpg");
final client = TusClient(
  file,  // Must be an XFile
  store: TusMemoryStore(), // Will not persist through device restarts. For persistent URL storage in memory see below
  maxChunkSize: 6 * 1024 * 1024, // 6MB chunks
  retries: 5,
  retryScale: RetryScale.exponential,
  retryInterval: 2,
  debug: true, // Will debug both the store and the client
);

2. Starting an Upload #

await client.upload(
  uri: Uri.parse("https://your-tus-server.com/files/"),
  onStart: (TusClient client, Duration? estimate) {
    print("Upload started; estimated time: ${estimate?.inSeconds} seconds");
  },
  onProgress: (double progress, Duration estimate) {
    print("Progress: ${progress.toStringAsFixed(1)}%, estimated time: ${estimate.inSeconds} seconds");
  },
  onComplete: () {
    print("Upload complete!");
    print("File URL: ${client.uploadUrl}");
  },
  headers: {"Authorization": "Bearer your_token"},
  metadata: {"cacheControl": "3600"},
  measureUploadSpeed: true,
  preventDuplicates: true, // NEW: Prevents creating duplicate uploads of the same file
);

3. Pausing an Upload #

Upload will pause after the current chunk finishes. For example:

print("Pausing upload...");
await client.pauseUpload();

4. Resuming an Upload #

If the upload has been paused, you can resume using:

// Resume with the same callbacks as original upload
await client.resumeUpload();

// Resume with a new progress callback
await client.resumeUpload(
  onProgress: (progress, estimate) {
    print("New progress handler: $progress%");
  }
);

// Clear the progress callback while keeping others
await client.resumeUpload(
  clearProgressCallback: true
);

// Replace some callbacks and clear others
await client.resumeUpload(
  onComplete: () => print("New completion handler"),
  clearProgressCallback: true
);

// Clear all callbacks
await client.clearAllCallbacks();
await client.resumeUpload();

5. Canceling an Upload #

Cancel the current upload and remove any saved state:

final result = await client.cancelUpload(); // Returns true when successful
if (result) {
  print("Upload canceled successfully.");
}

6. Using TusFileStore (Native Platforms) #

On mobile/desktop, you can persist the upload progress to the file system.

import 'package:path_provider/path_provider.dart';
import 'dart:io';

final tempDir = await getTemporaryDirectory();
final tempDirectory = Directory('${tempDir.path}/${file.name}_uploads');
if (!tempDirectory.existsSync()) {
  tempDirectory.createSync(recursive: true);
}

final client = TusClient(
  file,
  store: TusFileStore(tempDirectory),
);
await client.upload(uri: Uri.parse("https://your-tus-server.com/files/"));

7. Using TusIndexedDBStore (Web) #

For web applications, use IndexedDB for persistent upload state:

import 'package:flutter/foundation.dart' show kIsWeb;

final store = kIsWeb ? TusIndexedDBStore() : TusMemoryStore();

final client = TusClient(
  file,
  store: store,
);
await client.upload(uri: Uri.parse("https://your-tus-server.com/files/"));

8. File Selection on Web #

For web applications, you have two main options for handling files:

Option 1: Using Any XFile with Loaded Bytes

import 'package:file_picker/file_picker.dart';

final result = await FilePicker.platform.pickFiles(
    withData: true, // Load bytes into memory. Works for small files
);

if (result == null) {
    return null;
}

final fileWithBytes = result.files.first.xFile; // This returns an XFile with bytes

// Create client with any XFile that has bytes loaded
final client = TusClient(
  fileWithBytes,  // Any XFile with bytes already loaded
  store: TusMemoryStore(), //This TusMemoryStore doesn't persist on reboots.
);

await client.upload(uri: Uri.parse("https://tus.example.com/files"));

Option 2: Using pickWebFilesForUpload()

This is a built-in method that will open a file picker on web and convert the files to a streamable XFile using Blob.

final result = await pickWebFilesForUpload(
    allowMultiple: true,
    acceptedFileTypes: ['*']  
)

if (result == null) {
    return null;
}

// Create client with any XFile that has bytes loaded
final client = TusClient(
  result.first,  // Streaming ready XFile
  store: TusMemoryStore(), 
);

await client.upload(uri: Uri.parse("https://tus.example.com/files"));

9. Using TusUploadManager #

The TusUploadManager provides a convenient way to manage multiple uploads with features like automatic queuing, status tracking, and batch operations.

Creating the Upload Manager

import 'package:another_tus_client/another_tus_client.dart';
import 'package:flutter/foundation.dart' show kIsWeb;

// Create a persistent store for upload state
final store = kIsWeb ? TusIndexedDBStore() : TusFileStore(await getUploadDirectory());

// Initialize the manager
final uploadManager = TusUploadManager(
  serverUrl: Uri.parse("https://your-tus-server.com/files/"),
  store: store,
  maxConcurrentUploads: 3,  // Control how many uploads run simultaneously
  autoStart: true,           // Start uploads as soon as they're added (default)
  measureUploadSpeed: true,  // Estimate upload time
  retries: 3,                // Retry failed uploads 3 times
  retryScale: RetryScale.exponential,
  retryInterval: 2,          // Wait 2, 4, 8 seconds between retries
  preventDuplicates: true,   // Prevent creating duplicate uploads
  debug: true, // Will debug manager, client and store
);

Adding Files to Upload

// Add a single file. Returns a custom ID for this upload
final uploadId1 = await uploadManager.addUpload(
  file1,
  metadata: {
    "bucketName": "user_files",
    "cacheControl": "3600",
    "contentType": file1.mimeType ?? "application/octet-stream"
  },
  headers: {
    "x-custom-header": "value"  // Add upload-specific headers
  }
);

// Add another file with different settings
final uploadId2 = await uploadManager.addUpload(
  file2,
  metadata: {"bucketName": "images"}
);

Listening to Upload Events

// Listen to upload status changes and progress updates
uploadManager.uploadEvents.listen((UploadEvent event) {
  final upload = event.upload;
  final type = event.eventType;
  print("Upload ID: ${upload.id}");
  print("Event: ${type}"); // start, resume, pause, progress, complete, error, cancel, add
  print("Status: ${upload.status}");  // ready, uploading, paused, completed, failed, cancelled
  print("Progress: ${upload.progress}%");
  print("Estimated time: ${upload.estimate.inSeconds} seconds");
  
  if (upload.status == UploadStatus.completed) {
    print("Upload URL: ${upload.client.uploadUrl}");
  } else if (upload.status == UploadStatus.failed) {
    print("Error: ${upload.error}");
  }
});

Controlling Individual Uploads

// Pause a specific upload
await uploadManager.pauseUpload(uploadId1);

// Resume a paused upload
await uploadManager.resumeUpload(uploadId1);

// Cancel an upload
await uploadManager.cancelUpload(uploadId2);

// Check for specific upload
final upload = uploadManager.getUpload(uploadId1);
if (upload != null && upload.status == UploadStatus.paused) {
  // Check if it's resumable
  final isResumable = await upload.client.isResumable();
  print("Can resume: $isResumable");
}

Batch Operations

// Pause all active uploads
await uploadManager.pauseAll();

// Resume all paused uploads
await uploadManager.resumeAll();

// Cancel all uploads
await uploadManager.cancelAll();

// Get all uploads
final allUploads = uploadManager.getAllUploads();
print("Total uploads: ${allUploads.length}");

Cleanup

// Clean up resources when done
@override
void dispose() {
  uploadManager.dispose();
  super.dispose();
}

Maintainers #

1
likes
140
points
195
downloads

Publisher

verified publisherbamboodigital.ca

Weekly Downloads

A web compatible tus client for dart allowing resumable uploads using the tus protocol.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

cross_file, crypto, flutter, http, path, speed_test_dart, universal_io, web

More

Packages that depend on another_tus_client