C# SFTP客户端实现文件传输

一、SFTP信息

SSH:加密的远程登陆/命令通道协议,可以链接到另一台机器执行命令,也可以传输一些文件;

SFTP:跑在SSH上面的文件传输协议,端口为22;

FTP:1971年的明文传输协议,端口21,与SFTP没有关系;

二、服务端OpenSSH

  1. systemctl enable --now ssh:开机自启 + 立即启动,启动的服务名为ssh.service

  2. /etc/init.d/sshd start:运行sshd脚本,start为参数,还有stop啥的

  3. ssh-keygen -A:生成密钥ssh_host_*_key,在服务端与客户端进行连接时,客户端发给服务端由它生成的公钥,服务端会留存这个公钥以识别OpenSSH

  4. /etc/ssh/sshd_config为SSH的配置文件,里面有端口、登陆账号(即Linux账号)、密钥等;

三、客户端

  1. 加载远程文件\目录列表

    SftpClient client = new SftpClient(ip, port, userName, password); // 除了port为int外,其他参数都是string,用户名和密码是Linux允许访问OpenSSH账号名和密码,sshd_config AllowUser那里做配置的
    client.Connect();
    List entries = client.ListDirectory(path) // path服务端的路径
    .Where(t => t.Name != "." && t.Name != ".." && !t.Name.StartsWith('.')) // 剔除"."当前目录、".."上级目录、"."开头的配置文件
    .OrderByDescending(t => t.IsDirectory)
    .ThenBy(t => t.Name, StringComparer.OrdinalIgnoreCase)
    .Select(t => mew TransFileInfo()
    {
    Name = t.Name,
    Size = t.IsDirectory ? 0 : t.Length,
    Time = t.LastWriteTime.ToString("yyyy-MM-dd HH:mm"),
    IsDirectory = t.IsDirectory,
    FullPath = t.Name
    });

  2. 删除远程文件\目录

    SftpClient client = new SftpClient(ip, port, userName, password);
    client.Connect(); // SftpClient的创建和连接的成本很低,可以在每次执行时都创建并连接
    SftpFileAttributes attrs = client.GetAttributes(deleteFilePath);
    if (attrs.IsDirectory)
    // 写个递归删除所有文件夹中的文件就行
    else
    client.DeleteFile(deleteFilePath);

  3. 重命名

    client.RenameFile(oldFilePath, newPath);

  4. 创建文件夹

    client.CreateDirectory(dirPath);

  5. 下载

    long totalLength = client.GetAttributes(remotePath).Size; // 获取一下远程文件的大小
    string tempPath = localPath + ".tmp"; // 先将远程文件保存至临时文件,结束后再删除掉.tmp后缀
    long offset = 0;
    if (File.Exits(tempPath))
    {
    offset = (new FileInfo(tempPath)).Length; // 因为提供的断点续传功能,所以临时文件的长度作为偏置,续传时就从这里开始
    if (offset >= totalLength)
    offset = totalLength; // 偏置为totalLength就算是下载完毕了
    }

    using StfpFileStream remoteStream = client.OpenRead(remotePath);
    remoteStream.Seek(offset, SeekOrigin.Begin); // SeekOrigin.Begin从文件开头开始偏移

    using FileStream localStream = new FileStream(tempPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, 256 * 1024); // 每次只读取256kb
    localStream.Seek(offset, SeekOrigin.Begin);

    byte[] buffer = new byte[256 * 1024];
    while(true)
    {
    if(!await Check())
    continue;

    复制代码
     int read = await remoteStream.ReadAsync(buffer.AsMemory(0, buffer.Length));  // Memory类似(指针 + 长度),由GC回收
     if (read == 0)
         break;
    
     await localStream.WriteAsync(buffer.AsMemory(0, read));

    }

    // 回收流资源 + 复制临时文件
    localStream.Dispose();
    if (File.Exits(localPath))
    File.Delete(localPath);

    File.Move(tempPath, localPath);

    async void Check()
    {
    TaskCompletionSource? tcs;
    lock(_lock)
    {
    if (isPaused)
    return false; // 这里省略了,isPaused为true时,while(true)那边不继续

    复制代码
         tcs = _pauseTcs;
     }
    
     if (tcs != null)
         await tcs.Task.ConfigureAwait(false);  // 在这里等

    }

    void Resume()
    {
    TaskCompletionSource? tcs;
    lock(_lock)
    {
    if (!isPaused)
    return;

    复制代码
         isPaused = false;
         tcs = _pauseTcs;
         _pauseTcs = null;
     }
    
     tcs?.TrySetResult(true);    

    }

    void Cancel()
    {
    TaskCompletionSource? tcs;
    lock(_lock)
    {
    if (isPaused)
    return;

    复制代码
         isPaused = true;
         _pauseTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationAsynchronously);
     }
    
     tcs?.TrySetResult(false);    

    }

相关推荐
小羊没烦恼!15 分钟前
OS X 上使用.NET开发应用程序
开发语言·前端·数据库·人工智能·c#·策略模式
Behavior2 小时前
Unity 7.0 Alpha 拆解:编辑器干掉 Mono 换 CoreCLR
c#·unity3d·游戏开发
淡海水2 小时前
02-06-原理篇-增量式GC与并发GC
算法·unity·c#·并发·gc·增量
Vertira3 小时前
C# 函数封装成 DLL,
开发语言·c#
浅尝辄止;4 小时前
设计模式在HIS/LIS医疗系统中的完整实践指南(C#版)
开发语言·设计模式·c#
kimloner14 小时前
开源一个 376KB 的 Windows 桌面小工具:便签 + 截图标注 + OCR + 待办提醒(C# / WinForms)
windows·开源·c#·ocr·winforms
阿松爱学习15 小时前
【Unity开发】Aspose.PDF的使用
unity·pdf·c#
半亩码田15 小时前
C#转Python第4.8篇:正则表达式:Python re 模块 vs C# System.Text.RegularExpressions
python·正则表达式·c#
FPGA小徐16 小时前
【一生一芯 / PA】异常响应机制:RISC-V 中 ecall → mtvec → mret 的完整代
开发语言·数据库·c#
看着博客敲代码16 小时前
No space left on device:服务器被 SSH 爆破到日志写满的完整排查与止血
linux·运维·服务器·安全·ssh