From 956450518e9e113d14ac05a30e91db980e4f31b1 Mon Sep 17 00:00:00 2001 From: artem Date: Mon, 13 May 2024 16:36:27 +0300 Subject: [PATCH] feat: add download/upload progress callback from proposal https://github.com/crifurch/pure_ftp/pull/8 --- lib/src/file_system/entries/ftp_file.dart | 3 +- lib/src/file_system/ftp_file_system.dart | 61 +++++++++++++++++------ lib/src/file_system/ftp_transfer.dart | 31 +++++++++--- 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/lib/src/file_system/entries/ftp_file.dart b/lib/src/file_system/entries/ftp_file.dart index 10b428e..d8485a2 100644 --- a/lib/src/file_system/entries/ftp_file.dart +++ b/lib/src/file_system/entries/ftp_file.dart @@ -34,8 +34,9 @@ class FtpFile extends FtpEntry { var result = false; try { final downloadFileStream = _client.fs.downloadFileStream(this); + //todo search way to provide file size if necessary final uploadStream = - secondClient.fs.uploadFileFromStream(fileTo, downloadFileStream); + secondClient.fs.uploadFileFromStream(fileTo, downloadFileStream, 0); result = await uploadStream; } finally { try { diff --git a/lib/src/file_system/ftp_file_system.dart b/lib/src/file_system/ftp_file_system.dart index 7d2af6c..c59e519 100644 --- a/lib/src/file_system/ftp_file_system.dart +++ b/lib/src/file_system/ftp_file_system.dart @@ -1,5 +1,3 @@ -// ignore_for_file: constant_identifier_names - import 'dart:async'; import 'package:pure_ftp/src/file_system/entries/ftp_directory.dart'; @@ -16,6 +14,9 @@ import 'package:pure_ftp/src/ftp/utils/data_parser_utils.dart'; import 'package:pure_ftp/src/ftp_client.dart'; import 'package:pure_ftp/utils/list_utils.dart'; +export 'package:pure_ftp/src/file_system/ftp_transfer.dart' + show OnTransferProgress; + typedef DirInfoCache = MapEntry>>; @@ -197,21 +198,42 @@ class FtpFileSystem { .toList(); }); - Stream> downloadFileStream(FtpFile file) => - _transfer.downloadFileStream(file); + Stream> downloadFileStream( + FtpFile file, { + OnTransferProgress? onReceiveProgress, + }) => + _transfer.downloadFileStream( + file, + onReceiveProgress: onReceiveProgress, + ); - Future> downloadFile(FtpFile file) async { + Future> downloadFile( + FtpFile file, { + OnTransferProgress? onReceiveProgress, + }) async { final result = []; - await downloadFileStream(file).listen(result.addAll).asFuture(); + await downloadFileStream( + file, + onReceiveProgress: onReceiveProgress, + ).listen(result.addAll).asFuture(); return result; } - Future uploadFile(FtpFile file, List data, - [UploadChunkSize chunkSize = UploadChunkSize.kb4]) async { + Future uploadFile( + FtpFile file, + List data, { + UploadChunkSize chunkSize = UploadChunkSize.kb4, + OnTransferProgress? onUploadProgress, + }) async { final stream = StreamController>(); var result = false; try { - final future = uploadFileFromStream(file, stream.stream); + final future = uploadFileFromStream( + file, + stream.stream, + data.length, + onUploadProgress: onUploadProgress, + ); if (data.isEmpty) { stream.add(data); } else { @@ -229,6 +251,20 @@ class FtpFileSystem { return result; } + Future uploadFileFromStream( + FtpFile file, + Stream> stream, + int fileSize, { + OnTransferProgress? onUploadProgress, + }) async { + return _transfer.uploadFileStream( + file, + stream, + fileSize, + onUploadProgress: onUploadProgress, + ); + } + Future getEntryInfo(FtpEntry entry, {bool fromCache = true}) async { assert( @@ -248,11 +284,6 @@ class FtpFileSystem { throw UnimplementedError(); } - Future uploadFileFromStream( - FtpFile file, Stream> stream) async { - return _transfer.uploadFileStream(file, stream); - } - FtpFileSystem copy() { final ftpFileSystem = FtpFileSystem(client: _client.clone()); ftpFileSystem._currentDirectory = _currentDirectory; @@ -267,7 +298,9 @@ class FtpFileSystem { } enum ListType { + // ignore: constant_identifier_names LIST(FtpCommand.LIST), + // ignore: constant_identifier_names MLSD(FtpCommand.MLSD), ; diff --git a/lib/src/file_system/ftp_transfer.dart b/lib/src/file_system/ftp_transfer.dart index 4826a35..2c7ba3c 100644 --- a/lib/src/file_system/ftp_transfer.dart +++ b/lib/src/file_system/ftp_transfer.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math'; import 'dart:typed_data'; import 'package:pure_ftp/src/file_system/entries/ftp_file.dart'; @@ -8,6 +9,8 @@ import 'package:pure_ftp/src/ftp/ftp_commands.dart'; import 'package:pure_ftp/src/ftp/ftp_socket.dart'; import 'package:pure_ftp/src/socket/common/client_raw_socket.dart'; +typedef OnTransferProgress = Function(int bytes, int total, double percents); + class FtpTransfer { final FtpSocket _socket; @@ -15,15 +18,18 @@ class FtpTransfer { required FtpSocket socket, }) : _socket = socket; - Stream> downloadFileStream(FtpFile file) { + Stream> downloadFileStream( + FtpFile file, { + OnTransferProgress? onReceiveProgress, + }) { final stream = StreamController>(); unawaited(_socket.openTransferChannel((socketFuture, log) async { + final fileSize = await file.size(); FtpCommand.RETR.write( _socket, [file.path], ); //will be closed by the transfer channel - // ignore: close_sinks final socket = await socketFuture; final response = await _socket.read(); @@ -32,11 +38,14 @@ class FtpTransfer { if (!transferCompleted) { throw FtpException('Error while downloading file'); } - var total = 0; + var downloaded = 0; await socket.listen( (event) { stream.add(event); - log?.call('Downloaded ${total += event.length} bytes'); + downloaded += event.length; + final total = max(fileSize, downloaded); + onReceiveProgress?.call(downloaded, total, downloaded / total * 100); + log?.call('Downloaded ${downloaded} of ${total} bytes'); }, ).asFuture(); await _socket.read(); @@ -48,7 +57,12 @@ class FtpTransfer { return stream.stream; } - Future uploadFileStream(FtpFile file, Stream> data) => + Future uploadFileStream( + FtpFile file, + Stream> data, + int fileSize, { + OnTransferProgress? onUploadProgress, + }) => _socket.openTransferChannel((socketFuture, log) async { FtpCommand.STOR.write( _socket, @@ -64,12 +78,15 @@ class FtpTransfer { if (!transferCompleted) { throw FtpException('Error while uploading file'); } - var total = 0; + var uploaded = 0; final transform = data.transform( StreamTransformer.fromHandlers( handleData: (event, sink) { sink.add(Uint8List.fromList(event)); - log?.call('Uploaded ${total += event.length} bytes'); + uploaded += event.length; + final total = max(fileSize, uploaded); + onUploadProgress?.call(uploaded, total, uploaded / total * 100); + log?.call('Downloaded ${uploaded} of ${total} bytes'); }, ), );