Published 2025-05-26
A Dart package for collecting files from SFTP servers with support for recursive traversal, maintaining folder structures, and automated file archiving. This package simplifies the process of downloading and managing files from SFTP sources.
Add the following to your pubspec.yaml:
dependencies:
pkg_sftp_file_collector: ^1.0.0 # Replace with the latest version
Then run:
dart pub get
Import the package:
import 'package:pkg_sftp_file_collector/pkg_sftp_file_collector.dart';
import 'dart:io';
import 'package:logging/logging.dart';
import 'package:pkg_sftp_file_collector/pkg_sftp_file_collector.dart';
Future<void> main() async {
// Setup logging
Logger.root.level = Level.FINE;
Logger.root.onRecord.listen((final LogRecord record) {
stdout.writeln('${record.level.name}: ${record.time}: [${record.loggerName}] ${record.message}');
});
// Configure SFTP connection using ModelSftpFileConnector
final ModelSftpFileConnector connectionDetails = ModelSftpFileConnector(
host: '192.168.22.42',
username: 'username',
passwordOrKey: '/path/to/.ssh/id_rsa', // or password string
port: 22,
remotePath: '/remote/data/source', // Source directory for operations like moveFile
sftpRootPath: '/remote/data/source', // Root path for listing and as base for relative paths
localPath: 'assets/', // Local directory to download files
sftpArchivePath: '/remote/data/archive', // Archive directory on the SFTP server
retainSftpFolderStructure: true,
sftpRootPathOnly: false, // Set to true to avoid recursive directory traversal in collectRecursive logic
sftpMoveToArchive: true, // Set to true to enable moving files to archive after download
);
final SftpFileCollector collector = SftpFileCollector(conn: connectionDetails);
try {
// Connect to the SFTP server
await collector.connect();
List<String> downloadedFiles;
if (connectionDetails.sftpRootPathOnly) {
// List files in the sftpRootPath
downloadedFiles = await collector.listFiles();
for (final String file in downloadedFiles) {
// Download each file
final (bool success, _) = await collector.downloadFile(fileName: file);
if (!success) {
// Handle download failure if necessary
Logger.root.warning('Failed to download $file');
// Remove from list if we only want to archive successfully downloaded files
// This part depends on desired behavior, for simplicity, keeping it as is.
}
}
} else {
// Download files recursively starting from conn.remotePath (which is sftpRootPath in this example)
downloadedFiles = await collector.collectRecursive(remotePath: connectionDetails.remotePath);
}
// Move downloaded files to archive if sftpMoveToArchive is true
if (connectionDetails.sftpMoveToArchive) {
for (final String file in downloadedFiles) {
// fileName for moveFile is relative to conn.remotePath
await collector.moveFile(fileName: file);
}
}
} catch (e) {
print('Error: $e');
} finally {
// Always disconnect when done
collector.disconnect();
}
}
If you only want to collect files from a specific directory without recursion, configure ModelSftpFileConnector accordingly:
Future<void> main() async {
// Configure for non-recursive collection
final ModelSftpFileConnector connectionDetails = ModelSftpFileConnector(
host: 'host',
username: 'username',
passwordOrKey: 'password', // or path to key file
port: 22,
sftpRootPath: '/remote/path', // The specific directory to list and download from
localPath: 'downloads/',
retainSftpFolderStructure: false, // Typically false for flat download structure
sftpRootPathOnly: true, // This flag is key for non-recursive behavior if using a combined logic
// but listFiles() itself is non-recursive.
// sftpMoveToArchive can be true or false based on requirements
// remotePath (source directory for moveFile, typically same as sftpRootPath if using file names from listFiles) and sftpArchivePath would be needed if sftpMoveToArchive is true
);
final SftpFileCollector collector = SftpFileCollector(conn: connectionDetails);
try {
// List and download files
await collector.connect();
// listFiles uses conn.sftpRootPath
final List<String> files = await collector.listFiles();
for (final String file in files) {
// downloadFile uses conn.sftpRootPath as base for fileName, and conn.localPath for destination
final (bool success, _) = await collector.downloadFile(fileName: file);
if (success) {
print('Downloaded $file successfully.');
// Optionally move to archive if configured
// if (connectionDetails.sftpMoveToArchive) {
// await collector.moveFile(fileName: file);
// }
} else {
print('Failed to download $file.');
}
}
} catch (e) {
print('Error: $e');
} finally {
collector.disconnect();
}
}
See the example/ folder for a complete example implementation.
This package is designed for reliable file collection from SFTP servers in production environments. It supports error handling, comprehensive logging, and is built on the dartssh2 package.
See the LICENSE file for details.