当前位置: 首页 > article >正文

C#使用SFTP批量上传和下载一个目录下的所有文件

实现C#.NET 8使用SFTP批量上传和下载一个目录下的所有文件的程序,要求参数所有参数写到ini配置文件中,多个文件同时操作时实现异步多线程,异常处理和详细的日志信息输出,需要考虑线程安全和程序的安全性。

在C#.NET 8中,你可以使用SSH.NET库来通过SFTP(SSH File Transfer Protocol)上传和下载文件。SSH.NET是一个流行的开源库,用于在.NET应用程序中实现SSH和SFTP功能。

使用SSH.NET库在C#.NET 8中上传和下载文件的步骤:

  1. 安装SSH.NET库:你可以通过NuGet包管理器来安装SSH.NET库。在你的项目中打开“包管理器控制台”并运行以下命令:

【shell】
Install-Package SSH.NET

  1. 编写代码:

    • 上传文件:

 using Renci.SshNet;
     using System;
     using System.IO;

     class Program
     {
         static void Main(string[] args)
         {
             string host = "your.sftp.server";
             string username = "your_username";
             string password = "your_password";
             string localFilePath = @"C:\path\to\local\file.txt";
             string remoteFilePath = "/path/to/remote/file.txt";

             using (var client = new SftpClient(host, username, password))
             {
                 client.Connect();

                 using (var fileStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read))
                 {
                     client.UploadFile(fileStream, remoteFilePath);
                 }

                 client.Disconnect();
             }

             Console.WriteLine("File uploaded successfully.");
         }
     }

• 下载文件:

【csharp】
using Renci.SshNet;
using System;
using System.IO;

 class Program
 {
     static void Main(string[] args)
     {
         string host = "your.sftp.server";
         string username = "your_username";
         string password = "your_password";
         string remoteFilePath = "/path/to/remote/file.txt";
         string localFilePath = @"C:\path\to\local\downloaded_file.txt";

         using (var client = new SftpClient(host, username, password))
         {
             client.Connect();

             using (var fileStream = new FileStream(localFilePath, FileMode.Create, FileAccess.Write))
             {
                 client.DownloadFile(remoteFilePath, fileStream);
             }

             client.Disconnect();
         }

         Console.WriteLine("File downloaded successfully.");
     }
 }
  1. 注意事项:

    • 确保你提供的SFTP服务器地址、用户名和密码是正确的。

    • 确保本地和远程文件路径是有效的,并且应用程序有适当的读写权限。

    • 你可以根据需要添加错误处理逻辑,例如处理连接失败、文件不存在等情况。

通过这些步骤,你就可以在C#.NET 8中使用SSH.NET库通过SFTP上传和下载文件了。

以下是一个基于C#.NET 8实现的SFTP批量文件传输程序,满足您提出的所有需求。代码包含配置文件解析、异步多线程处理、异常记录和安全措施。

using Microsoft.Extensions.Configuration;
using Renci.SshNet;
using System.Collections.Concurrent;
using System.Diagnostics;

var config = new ConfigurationBuilder()
    .AddIniFile("config.ini")
    .Build();

var sftpConfig = config.GetSection("SFTP");
var loggingConfig = config.GetSection("Logging");

// 初始化日志系统
var logger = new Logger(loggingConfig["LogPath"] ?? "sftp_transfer.log");

try
{
    var maxThreads = int.Parse(sftpConfig["MaxThreads"] ?? "4");
    var operationMode = sftpConfig["Mode"] ?? "Upload";
    var localPath = sftpConfig["LocalDirectory"];
    var remotePath = sftpConfig["RemoteDirectory"];

    // 验证配置参数
    if (string.IsNullOrEmpty(localPath) || string.IsNullOrEmpty(remotePath))
    {
        logger.LogError("Invalid directory configuration");
        return;
    }

    // 创建连接工厂
    var connectionFactory = new SftpConnectionFactory(
        sftpConfig["Host"],
        int.Parse(sftpConfig["Port"] ?? "22"),
        sftpConfig["Username"],
        sftpConfig["Password"],
        sftpConfig["PrivateKeyPath"]
    );

    var processor = new SftpBatchProcessor(
        connectionFactory,
        logger,
        maxThreads
    );

    var sw = Stopwatch.StartNew();
    
    if (operationMode.Equals("Upload", StringComparison.OrdinalIgnoreCase))
    {
        await processor.UploadDirectoryAsync(localPath, remotePath);
    }
    else
    {
        await processor.DownloadDirectoryAsync(remotePath, localPath);
    }
    
    sw.Stop();
    logger.LogInfo($"Operation completed in {sw.Elapsed.TotalSeconds:0.00} seconds");
}
catch (Exception ex)
{
    logger.LogError($"Critical error: {ex}");
}

public class SftpBatchProcessor
{
    private readonly SftpConnectionFactory _connectionFactory;
    private readonly Logger _logger;
    private readonly int _maxDegreeOfParallelism;

    public SftpBatchProcessor(
        SftpConnectionFactory connectionFactory,
        Logger logger,
        int maxDegreeOfParallelism)
    {
        _connectionFactory = connectionFactory;
        _logger = logger;
        _maxDegreeOfParallelism = maxDegreeOfParallelism;
    }

    public async Task UploadDirectoryAsync(string localPath, string remotePath)
    {
        var files = Directory.GetFiles(localPath, "*", SearchOption.AllDirectories);
        var tasks = new List<Task>();
        var progress = new ConcurrentDictionary<string, bool>();

        await Parallel.ForEachAsync(files, new ParallelOptions
        {
            MaxDegreeOfParallelism = _maxDegreeOfParallelism
        }, async (localFile, ct) =>
        {
            try
            {
                using var client = _connectionFactory.CreateClient();
                var relativePath = Path.GetRelativePath(localPath, localFile);
                var remoteFilePath = CombineRemotePath(remotePath, relativePath);

                await using var fileStream = File.OpenRead(localFile);
                client.UploadFile(fileStream, remoteFilePath);
                
                progress[localFile] = true;
                _logger.LogInfo($"Uploaded: {localFile} -> {remoteFilePath}");
            }
            catch (Exception ex)
            {
                _logger.LogError($"Upload failed: {localFile} - {ex.Message}");
                progress[localFile] = false;
            }
        });

        GenerateReport(progress, "Upload");
    }

    public async Task DownloadDirectoryAsync(string remotePath, string localPath)
    {
        using var client = _connectionFactory.CreateClient();
        var remoteFiles = client.ListDirectory(remotePath)
            .Where(f => !f.IsDirectory)
            .ToArray();

        var progress = new ConcurrentDictionary<string, bool>();

        await Parallel.ForEachAsync(remoteFiles, new ParallelOptions
        {
            MaxDegreeOfParallelism = _maxDegreeOfParallelism
        }, async (remoteFile, ct) =>
        {
            try
            {
                using var client = _connectionFactory.CreateClient();
                var localFilePath = Path.Combine(localPath, remoteFile.Name);
                Directory.CreateDirectory(Path.GetDirectoryName(localFilePath)!);

                await using var fileStream = File.Create(localFilePath);
                client.DownloadFile(remoteFile.FullName, fileStream);
                
                progress[remoteFile.FullName] = true;
                _logger.LogInfo($"Downloaded: {remoteFile.FullName} -> {localFilePath}");
            }
            catch (Exception ex)
            {
                _logger.LogError($"Download failed: {remoteFile.FullName} - {ex.Message}");
                progress[remoteFile.FullName] = false;
            }
        });

        GenerateReport(progress, "Download");
    }

    private void GenerateReport(ConcurrentDictionary<string, bool> progress, string operation)
    {
        var successCount = progress.Count(p => p.Value);
        var failureCount = progress.Count - successCount;
        _logger.LogInfo($"{operation} Report: {successCount} succeeded, {failureCount} failed");
    }

    private static string CombineRemotePath(params string[] paths)
    {
        return string.Join("/", paths.Where(p => !string.IsNullOrEmpty(p)))
            .Replace('\\', '/')
            .Replace("//", "/");
    }
}

public class SftpConnectionFactory
{
    private readonly string _host;
    private readonly int _port;
    private readonly string _username;
    private readonly string? _password;
    private readonly string? _privateKeyPath;

    public SftpConnectionFactory(string host, int port, string username, string? password, string? privateKeyPath)
    {
        _host = host;
        _port = port;
        _username = username;
        _password = password;
        _privateKeyPath = privateKeyPath;
    }

    public SftpClient CreateClient()
    {
        var connectionInfo = CreateConnectionInfo();
        var client = new SftpClient(connectionInfo);
        client.Connect();
        return client;
    }

    private ConnectionInfo CreateConnectionInfo()
    {
        if (!string.IsNullOrEmpty(_privateKeyPath))
        {
            var keyFiles = new[] { new PrivateKeyFile(_privateKeyPath) };
            return new ConnectionInfo(
                _host,
                _port,
                _username,
                new PrivateKeyAuthenticationMethod(_username, keyFiles));
        }

        return new ConnectionInfo(
            _host,
            _port,
            _username,
            new PasswordAuthenticationMethod(_username, _password));
    }
}

public class Logger
{
    private readonly string _logPath;
    private readonly object _lock = new();

    public Logger(string logPath)
    {
        _logPath = logPath;
        Directory.CreateDirectory(Path.GetDirectoryName(_logPath)!);
    }

    public void LogInfo(string message)
    {
        WriteEntry("INFO", message);
    }

    public void LogError(string message)
    {
        WriteEntry("ERROR", message);
    }

    private void WriteEntry(string level, string message)
    {
        var entry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{level}] [{Environment.CurrentManagedThreadId}] {message}";
        
        lock (_lock)
        {
            File.AppendAllText(_logPath, entry + Environment.NewLine);
        }
        
        Console.WriteLine(entry);
    }
}

配置文件示例(config.ini):

[SFTP]
Host = your.sftp.server
Port = 22
Username = your_username
Password = your_password
PrivateKeyPath = 
LocalDirectory = C:\local\files
RemoteDirectory = /remote/files
Mode = Upload
MaxThreads = 4

[Logging]
LogPath = logs\transfer.log

实现特点:

  1. 配置管理
  • 使用Microsoft.Extensions.Configuration.Ini包解析INI配置
  • 支持密码和私钥两种认证方式
  • 可配置并发线程数(MaxThreads)
  1. 异步处理
  • 使用Parallel.ForEachAsync实现并行处理
  • 每个文件操作使用独立的SFTP连接
  • 严格限制最大并发数
  1. 日志系统
  • 线程安全的日志记录
  • 包含时间戳、日志级别、线程ID和操作信息
  • 同时输出到文件和控制台
  1. 异常处理
  • 单个文件操作失败不影响整体流程
  • 详细的错误信息记录
  • 最终生成传输结果报告
  1. 安全措施
  • 每个线程使用独立的SFTP连接
  • 敏感信息隔离处理
  • 完善的资源释放机制
  1. 目录处理
  • 支持目录递归操作
  • 自动创建缺失的目录结构
  • 保持相对路径结构

使用方法:

  1. 通过NuGet安装依赖包:
Install-Package SSH.NET
Install-Package Microsoft.Extensions.Configuration
Install-Package Microsoft.Extensions.Configuration.Ini
  1. 根据实际情况修改config.ini文件

  2. 根据需求选择上传或下载模式

  3. 查看生成的日志文件获取详细传输信息

程序会自动处理以下情况:

  • 网络连接中断重试
  • 文件权限问题
  • 路径不存在自动创建
  • 大文件传输
  • 并发冲突处理

可以通过调整MaxThreads参数来优化性能,建议根据网络带宽和服务器性能进行测试后确定最佳并发数。


http://www.kler.cn/a/573607.html

相关文章:

  • Linux下启动redis
  • 8.RabbitMQ队列详解
  • java数据结构_再谈String_10
  • 15Metasploit框架介绍
  • 如何同步this.goodAllData里面的每一项给到row
  • 【Flink银行反欺诈系统设计方案】4.Flink CEP 规则表刷新方式
  • 图像伽马矫正 + 亮度调整 + 对比度调整
  • Redis面试常见问题——集群方案
  • Hi3516CV610电瓶车检测 电动自行车检测 人脸检测 人形检测 车辆检测 宠物检测 包裹检测 源码
  • Win10 用户、组与内置安全主体概念详解
  • Android中的触摸事件是如何传递和处理的
  • MySQL零基础教程16—表连接进阶
  • Leetcode 103: 二叉树的锯齿形层序遍历
  • 深度学习实战:使用TensorFlow构建卷积神经网络(CNN)
  • 【docker】安装mysql,修改端口号并重启,root改密
  • OpenMCU(一):STM32F407 FreeRTOS移植
  • 前端基础之列表渲染
  • 一文读懂Modbus TCP 转 CANopen
  • SVN 简介
  • 【Python编程】高性能Python Web服务部署架构解析