From 075003f1ad355697387335c2a0678e7f99daed1b Mon Sep 17 00:00:00 2001 From: RafayAhmad7548 Date: Thu, 14 Aug 2025 06:22:05 +0500 Subject: [PATCH] make sftpworker upload/download action handle single file --- lib/sftp_explorer.dart | 24 +++--- lib/sftp_worker.dart | 124 ++++++++++++++--------------- lib/widgets/operation_buttons.dart | 18 +++-- 3 files changed, 77 insertions(+), 89 deletions(-) diff --git a/lib/sftp_explorer.dart b/lib/sftp_explorer.dart index 9c50dc4..00d6948 100644 --- a/lib/sftp_explorer.dart +++ b/lib/sftp_explorer.dart @@ -216,23 +216,17 @@ class _SftpExplorerState extends State { final files = await openFiles(); filePaths = files.map((file) => file.path).toList(); } - try { - bool start = true; - await for (final progress in widget.sftpWorker.uploadFiles(path, filePaths)) { - if (start) { - start = false; - _listDir(); - } - setState(() => _uploadProgress = progress); - if (progress == 1) { - // TODO: fix: next file also starts to show - _listDir(); + for (final filePath in filePaths) { + try { + await for (final progress in widget.sftpWorker.uploadFile(path, filePath)) { + setState(() => _uploadProgress = progress); } + await _listDir(); } - } - catch (e) { - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar(buildErrorSnackBar(context, e.toString())); + catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(buildErrorSnackBar(context, e.toString())); + } } } setState(() => _uploadProgress = null); diff --git a/lib/sftp_worker.dart b/lib/sftp_worker.dart index eff00d6..0221c94 100644 --- a/lib/sftp_worker.dart +++ b/lib/sftp_worker.dart @@ -15,11 +15,11 @@ class ListDir extends SftpCommand { ListDir(this.path); } -class UploadFiles extends SftpCommand { +class UploadFile extends SftpCommand { final String path; - final List filePaths; + final String filePath; - UploadFiles(this.path, this.filePaths); + UploadFile(this.path, this.filePath); } class MkDir extends SftpCommand { @@ -42,12 +42,12 @@ class Rename extends SftpCommand { Rename(this.oldpath, this.newpath); } -class DownloadFiles extends SftpCommand { - final List files; +class DownloadFile extends SftpCommand { + final SftpName file; final String path; final String downloadPath; - DownloadFiles(this.files, this.path, this.downloadPath); + DownloadFile(this.file, this.path, this.downloadPath); } @@ -120,33 +120,30 @@ class SftpWorker { on SftpStatusError catch (e) { sendPort.send((id, RemoteError(e.message, ''))); } - case UploadFiles(:final path, :final filePaths): - for (var filePath in filePaths) { - try { - final file = File(filePath); - final fileSize = await file.length(); - final remoteFile = await sftpClient.open( - '$path${basename(filePath)}', - mode: SftpFileOpenMode.create | SftpFileOpenMode.write | SftpFileOpenMode.exclusive - ); - bool timeout = true; - await remoteFile.write( - file.openRead().cast(), - onProgress: (progress) { - if (timeout) { - timeout = false; - sendPort.send((id, progress/fileSize)); - Future.delayed(Duration(seconds: 2), () => timeout = true); - } + case UploadFile(:final path, :final filePath): + try { + final file = File(filePath); + final fileSize = await file.length(); + final remoteFile = await sftpClient.open( + '$path${basename(filePath)}', + mode: SftpFileOpenMode.create | SftpFileOpenMode.write | SftpFileOpenMode.exclusive + ); + bool timeout = true; + await remoteFile.write( + file.openRead().cast(), + onProgress: (progress) { + if (timeout) { + timeout = false; + sendPort.send((id, progress/fileSize)); + Future.delayed(Duration(seconds: 2), () => timeout = true); } - ); - } - on SftpStatusError catch (e) { - sendPort.send((id, RemoteError(e.message, ''))); - } - sendPort.send((id, 1.0)); + } + ); } - sendPort.send((id, null)); + on SftpStatusError catch (e) { + sendPort.send((id, RemoteError(e.message, ''))); + } + sendPort.send((id, 1.0)); case MkDir(:final path): try { await sftpClient.mkdir(path); @@ -190,42 +187,39 @@ class SftpWorker { on SftpStatusError catch (e) { sendPort.send((id, RemoteError(e.message, ''))); } - case DownloadFiles(:final files, :final path, :final downloadPath): - for (final file in files) { - try { - final localFile = File('$downloadPath/${file.filename}'); - if (await localFile.exists()) { - sendPort.send((id, RemoteError('File Already Exists', ''))); - continue; - } - final localFileWriter = await localFile.open(mode: FileMode.write); - final remoteFile = await sftpClient.open('$path${file.filename}'); - final fileSize = file.attr.size!; - bool timeout = true; - await for (final bytes in remoteFile.read( - onProgress: (progress) { - if (timeout) { - timeout = false; - sendPort.send((id, progress/fileSize)); - Future.delayed(Duration(seconds: 2), () => timeout = true); - } + case DownloadFile(:final file, :final path, :final downloadPath): + try { + final localFile = File('$downloadPath/${file.filename}'); + if (await localFile.exists()) { + sendPort.send((id, RemoteError('File Already Exists', ''))); + break; + } + final localFileWriter = await localFile.open(mode: FileMode.write); + final remoteFile = await sftpClient.open('$path${file.filename}'); + final fileSize = file.attr.size!; + bool timeout = true; + await for (final bytes in remoteFile.read( + onProgress: (progress) { + if (timeout) { + timeout = false; + sendPort.send((id, progress/fileSize)); + Future.delayed(Duration(seconds: 2), () => timeout = true); } - )) { - await localFileWriter.writeFrom(bytes); } + )) { + await localFileWriter.writeFrom(bytes); } - on SftpStatusError catch (e) { - sendPort.send((id, RemoteError(e.message, ''))); - } - sendPort.send((id, 1.0)); } - sendPort.send((id, null)); + on SftpStatusError catch (e) { + sendPort.send((id, RemoteError(e.message, ''))); + } + sendPort.send((id, 1.0)); } }); } void _sftpResponseHandler(dynamic message) { - final (int id, Object? response) = message; + final (int id, Object response) = message; if (_activeRequests[id] is Completer) { final completer = _activeRequests.remove(id)!; @@ -243,13 +237,11 @@ class SftpWorker { controller.addError(response); } else { - if (response == null) { + controller.add(response); + if (response == 1) { controller.close(); _activeRequests.remove(id); } - else { - controller.add(response); - } } } } @@ -264,11 +256,11 @@ class SftpWorker { } - Stream uploadFiles(String path, List filePaths) { + Stream uploadFile(String path, String filePath) { final controller = StreamController(); final id = _idCounter++; _activeRequests[id] = controller; - _commands.send((id, UploadFiles(path, filePaths))); + _commands.send((id, UploadFile(path, filePath))); return controller.stream; } @@ -296,11 +288,11 @@ class SftpWorker { await completer.future; } - Stream downloadFiles(List files, String path, String downloadPath) { + Stream downloadFile(SftpName file, String path, String downloadPath) { final controller = StreamController(); final id = _idCounter++; _activeRequests[id] = controller; - _commands.send((id, DownloadFiles(files, path, downloadPath))); + _commands.send((id, DownloadFile(file, path, downloadPath))); return controller.stream; } diff --git a/lib/widgets/operation_buttons.dart b/lib/widgets/operation_buttons.dart index 5b09634..c720a79 100644 --- a/lib/widgets/operation_buttons.dart +++ b/lib/widgets/operation_buttons.dart @@ -123,17 +123,19 @@ class OperationButtons extends StatelessWidget { onPressed: () async { final downloadsDir = await getDownloadsDirectory(); if (downloadsDir == null) return; - try { - await for (final progress in sftpWorker.downloadFiles(dirEntries, path, downloadsDir.path)) { - setDownloadProgress(progress); + for (final dirEntry in dirEntries) { + try { + await for (final progress in sftpWorker.downloadFile(dirEntry, path, downloadsDir.path)) { + setDownloadProgress(progress); + } } - setDownloadProgress(null); - } - catch (e) { - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar(buildErrorSnackBar(context, e.toString())); + catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(buildErrorSnackBar(context, e.toString())); + } } } + setDownloadProgress(null); }, icon: Icon(Icons.download) )