make sftpworker upload/download action handle single file

This commit is contained in:
RafayAhmad7548 2025-08-14 06:22:05 +05:00
parent 33ade0479f
commit 075003f1ad
3 changed files with 77 additions and 89 deletions

View file

@ -216,23 +216,17 @@ class _SftpExplorerState extends State<SftpExplorer> {
final files = await openFiles(); final files = await openFiles();
filePaths = files.map((file) => file.path).toList(); filePaths = files.map((file) => file.path).toList();
} }
try { for (final filePath in filePaths) {
bool start = true; try {
await for (final progress in widget.sftpWorker.uploadFiles(path, filePaths)) { await for (final progress in widget.sftpWorker.uploadFile(path, filePath)) {
if (start) { setState(() => _uploadProgress = progress);
start = false;
_listDir();
}
setState(() => _uploadProgress = progress);
if (progress == 1) {
// TODO: fix: next file also starts to show
_listDir();
} }
await _listDir();
} }
} catch (e) {
catch (e) { if (context.mounted) {
if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar(buildErrorSnackBar(context, e.toString()));
ScaffoldMessenger.of(context).showSnackBar(buildErrorSnackBar(context, e.toString())); }
} }
} }
setState(() => _uploadProgress = null); setState(() => _uploadProgress = null);

View file

@ -15,11 +15,11 @@ class ListDir extends SftpCommand {
ListDir(this.path); ListDir(this.path);
} }
class UploadFiles extends SftpCommand { class UploadFile extends SftpCommand {
final String path; final String path;
final List<String> filePaths; final String filePath;
UploadFiles(this.path, this.filePaths); UploadFile(this.path, this.filePath);
} }
class MkDir extends SftpCommand { class MkDir extends SftpCommand {
@ -42,12 +42,12 @@ class Rename extends SftpCommand {
Rename(this.oldpath, this.newpath); Rename(this.oldpath, this.newpath);
} }
class DownloadFiles extends SftpCommand { class DownloadFile extends SftpCommand {
final List<SftpName> files; final SftpName file;
final String path; final String path;
final String downloadPath; 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) { on SftpStatusError catch (e) {
sendPort.send((id, RemoteError(e.message, ''))); sendPort.send((id, RemoteError(e.message, '')));
} }
case UploadFiles(:final path, :final filePaths): case UploadFile(:final path, :final filePath):
for (var filePath in filePaths) { try {
try { final file = File(filePath);
final file = File(filePath); final fileSize = await file.length();
final fileSize = await file.length(); final remoteFile = await sftpClient.open(
final remoteFile = await sftpClient.open( '$path${basename(filePath)}',
'$path${basename(filePath)}', mode: SftpFileOpenMode.create | SftpFileOpenMode.write | SftpFileOpenMode.exclusive
mode: SftpFileOpenMode.create | SftpFileOpenMode.write | SftpFileOpenMode.exclusive );
); bool timeout = true;
bool timeout = true; await remoteFile.write(
await remoteFile.write( file.openRead().cast(),
file.openRead().cast(), onProgress: (progress) {
onProgress: (progress) { if (timeout) {
if (timeout) { timeout = false;
timeout = false; sendPort.send((id, progress/fileSize));
sendPort.send((id, progress/fileSize)); Future.delayed(Duration(seconds: 2), () => timeout = true);
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): case MkDir(:final path):
try { try {
await sftpClient.mkdir(path); await sftpClient.mkdir(path);
@ -190,42 +187,39 @@ class SftpWorker {
on SftpStatusError catch (e) { on SftpStatusError catch (e) {
sendPort.send((id, RemoteError(e.message, ''))); sendPort.send((id, RemoteError(e.message, '')));
} }
case DownloadFiles(:final files, :final path, :final downloadPath): case DownloadFile(:final file, :final path, :final downloadPath):
for (final file in files) { try {
try { final localFile = File('$downloadPath/${file.filename}');
final localFile = File('$downloadPath/${file.filename}'); if (await localFile.exists()) {
if (await localFile.exists()) { sendPort.send((id, RemoteError('File Already Exists', '')));
sendPort.send((id, RemoteError('File Already Exists', ''))); break;
continue; }
} final localFileWriter = await localFile.open(mode: FileMode.write);
final localFileWriter = await localFile.open(mode: FileMode.write); final remoteFile = await sftpClient.open('$path${file.filename}');
final remoteFile = await sftpClient.open('$path${file.filename}'); final fileSize = file.attr.size!;
final fileSize = file.attr.size!; bool timeout = true;
bool timeout = true; await for (final bytes in remoteFile.read(
await for (final bytes in remoteFile.read( onProgress: (progress) {
onProgress: (progress) { if (timeout) {
if (timeout) { timeout = false;
timeout = false; sendPort.send((id, progress/fileSize));
sendPort.send((id, progress/fileSize)); Future.delayed(Duration(seconds: 2), () => timeout = true);
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) { void _sftpResponseHandler(dynamic message) {
final (int id, Object? response) = message; final (int id, Object response) = message;
if (_activeRequests[id] is Completer) { if (_activeRequests[id] is Completer) {
final completer = _activeRequests.remove(id)!; final completer = _activeRequests.remove(id)!;
@ -243,13 +237,11 @@ class SftpWorker {
controller.addError(response); controller.addError(response);
} }
else { else {
if (response == null) { controller.add(response);
if (response == 1) {
controller.close(); controller.close();
_activeRequests.remove(id); _activeRequests.remove(id);
} }
else {
controller.add(response);
}
} }
} }
} }
@ -264,11 +256,11 @@ class SftpWorker {
} }
Stream<double> uploadFiles(String path, List<String> filePaths) { Stream<double> uploadFile(String path, String filePath) {
final controller = StreamController<double>(); final controller = StreamController<double>();
final id = _idCounter++; final id = _idCounter++;
_activeRequests[id] = controller; _activeRequests[id] = controller;
_commands.send((id, UploadFiles(path, filePaths))); _commands.send((id, UploadFile(path, filePath)));
return controller.stream; return controller.stream;
} }
@ -296,11 +288,11 @@ class SftpWorker {
await completer.future; await completer.future;
} }
Stream<double> downloadFiles(List<SftpName> files, String path, String downloadPath) { Stream<double> downloadFile(SftpName file, String path, String downloadPath) {
final controller = StreamController<double>(); final controller = StreamController<double>();
final id = _idCounter++; final id = _idCounter++;
_activeRequests[id] = controller; _activeRequests[id] = controller;
_commands.send((id, DownloadFiles(files, path, downloadPath))); _commands.send((id, DownloadFile(file, path, downloadPath)));
return controller.stream; return controller.stream;
} }

View file

@ -123,17 +123,19 @@ class OperationButtons extends StatelessWidget {
onPressed: () async { onPressed: () async {
final downloadsDir = await getDownloadsDirectory(); final downloadsDir = await getDownloadsDirectory();
if (downloadsDir == null) return; if (downloadsDir == null) return;
try { for (final dirEntry in dirEntries) {
await for (final progress in sftpWorker.downloadFiles(dirEntries, path, downloadsDir.path)) { try {
setDownloadProgress(progress); await for (final progress in sftpWorker.downloadFile(dirEntry, path, downloadsDir.path)) {
setDownloadProgress(progress);
}
} }
setDownloadProgress(null); catch (e) {
} if (context.mounted) {
catch (e) { ScaffoldMessenger.of(context).showSnackBar(buildErrorSnackBar(context, e.toString()));
if (context.mounted) { }
ScaffoldMessenger.of(context).showSnackBar(buildErrorSnackBar(context, e.toString()));
} }
} }
setDownloadProgress(null);
}, },
icon: Icon(Icons.download) icon: Icon(Icons.download)
) )