upload method
Start or resume an upload in chunks of maxChunkSize throwing
ProtocolException on server error
Implementation
Future<void> upload({
Function(double, Duration)? onProgress,
Function(TusClient, Duration?)? onStart,
Function()? onComplete,
required Uri uri,
Map<String, String>? metadata = const {},
Map<String, String>? headers = const {},
bool measureUploadSpeed = false,
bool preventDuplicates = true,
}) async {
_log('Starting upload process to $uri');
_log(
'Parameters: measureUploadSpeed=$measureUploadSpeed, preventDuplicates=$preventDuplicates');
setUploadData(uri, headers, metadata);
_log('Upload data set');
// Check for duplicates if requested
if (preventDuplicates) {
_log('Checking for duplicate uploads');
final (exists, canResume) = await checkExistingUpload();
_log('Existing upload check: exists=$exists, canResume=$canResume');
if (exists && !canResume) {
_log('Upload exists but cannot be resumed');
throw ProtocolException(
'An upload with the same fingerprint exists but cannot be resumed. '
'If you wish to force a new upload, set preventDuplicates to false.');
}
}
_log('Checking if upload is resumable');
final _isResumable = await isResumable();
_log('Is resumable: $_isResumable');
// Save the callbacks for possible resume.
_onProgress = onProgress;
_onComplete = onComplete;
_onStart = onStart;
_log('Callbacks saved');
if (measureUploadSpeed) {
_log('Measuring upload speed');
await setUploadTestServers();
await uploadSpeedTest();
}
if (!_isResumable) {
_log('Upload not resumable, creating new upload');
await createUpload();
} else {
_log('Upload is resumable, using existing upload');
}
// get offset from server
_log('Getting current offset from server');
_offset = await _getOffset();
_log('Current offset: $_offset bytes');
// Save the file size as an int in a variable to avoid having to call
int totalBytes = _fileSize as int;
_log('Total bytes to upload: $totalBytes');
// File existence check for non-web platforms before starting upload
if (!kIsWeb && _file.path.isNotEmpty) {
_log('Checking file existence: ${_file.path}');
try {
final file = File(_file.path);
if (!file.existsSync()) {
_log('Error: File not found');
throw Exception("Cannot find file ${_file.path.split('/').last}");
}
_log('File exists');
} catch (e) {
_log('Error accessing file: $e');
throw Exception("Cannot access file ${_file.path.split('/').last}: $e");
}
}
// We start a stopwatch to calculate the upload speed
_log('Starting upload stopwatch');
final uploadStopwatch = Stopwatch()..start();
// start upload
_log('Initializing HTTP client');
final client = getHttpClient();
if (onStart != null) {
_log('Calling onStart callback');
Duration? estimate;
if (uploadSpeed != null) {
final _workedUploadSpeed = uploadSpeed! * 1000000;
_log('Calculated upload speed: $_workedUploadSpeed bytes/s');
estimate = Duration(
seconds: (totalBytes / _workedUploadSpeed).round(),
);
_log('Estimated upload time: ${estimate.inSeconds} seconds');
}
// The time remaining to finish the upload
onStart(this, estimate);
}
_log('Starting upload loop');
while (!_pauseUpload && _offset < totalBytes) {
_log(
'Upload chunk: offset=$_offset, chunk=${min(maxChunkSize, totalBytes - _offset)} bytes');
// File existence check for non-web platforms before each chunk
if (!kIsWeb && _file.path.isNotEmpty) {
try {
final file = File(_file.path);
if (!file.existsSync()) {
_log('Error: File no longer exists');
throw Exception("Cannot find file ${_file.path.split('/').last}");
}
} catch (e) {
_log('Error accessing file during chunk upload: $e');
throw Exception(
"Cannot access file ${_file.path.split('/').last}: $e");
}
}
final uploadHeaders = Map<String, String>.from(headers ?? {})
..addAll({
"Tus-Resumable": tusVersion,
"Upload-Offset": "$_offset",
"Content-Type": "application/offset+octet-stream"
});
_log('Upload headers: $uploadHeaders');
await _performUpload(
onComplete: onComplete,
onProgress: onProgress,
uploadHeaders: uploadHeaders,
client: client,
uploadStopwatch: uploadStopwatch,
totalBytes: totalBytes,
);
}
if (_pauseUpload) {
_log('Upload paused at offset: $_offset / $totalBytes bytes');
} else {
_log('Upload loop completed');
}
}