[C#] 使用SSH.Net套件處理SFTP的上傳、下載、刪除…等操作

user SSH.Net handle SFTP download and upload file

前言

個人工作上的程式碼備份,應該有未盡完善的部份,日後再隨時回來修改程式碼

實作

環境是.NET Framework 4.8

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
/*NuGet要先加入Renci.SshNet的參考 */
using Renci.SshNet;
using Renci.SshNet.Sftp;

namespace Console_MobilizeUploadFile.Utility
{
    /*
     文章參考:
     [C#]SFTP (使用第三方套建 SSH.Net) - 類別庫為案例
     https://hackmd.io/@RoyChen/SFTP_SSHNet
     C# 使用 SSH.NET SFTP 上傳及下載範例
     https://www.tpisoftware.com/tpu/articleDetails/2645
     */
    public class MySshSftp
    {
        private SftpClient _sftp;
        /// <summary>
        /// SFTP 連接狀態
        /// </summary>
        public bool Connected { get { return _sftp.IsConnected; } }

        /// <summary>
        /// 建構子
        /// </summary>
        /// <param name="ip">IP</param>
        /// <param name="port">端口</param>
        /// <param name="account">id</param>
        /// <param name="pwd">passwd</param>
        public MySshSftp(string ip, string account, string pwd)
        {
            _sftp = new SftpClient(ip, port: 22, account, pwd);
        }

        /// <summary>
        /// SFTP 連線
        /// </summary>
        /// <returns>true成功</returns>
        public void Connect()
        {
            
            if (!Connected)
            {
                _sftp.Connect();
            } 
                 
        }

        /// <summary>
        /// SFTP 關閉
        /// </summary> 
        public void Disconnect()
        {
          if (_sftp != null && Connected)
          {
             _sftp.Disconnect();
          }
              
        }

        /// <summary>
        /// 檢查指定的遠端目錄是否存在,如果不存在就遞迴創建。
        /// </summary>
        /// <param name="remoteDirectory"></param>
        public void CreateDirectoryIfNotExists(string remoteDirectory)
        {
            var directories = remoteDirectory.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            string currentPath = "/";
            _sftp.Connect();
            foreach (var dir in directories)
            {
                currentPath = $"{currentPath}{dir}/"; 
                if (_sftp.Exists(currentPath)==false)
                {//不存在目錄
                    //建立目錄
                    _sftp.CreateDirectory(currentPath);
                }
            }//end foreach
            _sftp.Disconnect();

        }
        /// <summary>
        /// SFTP 上傳檔案
        /// </summary>
        /// <param name="localPath">本機檔案 (要上傳的檔案)</param>
        /// <param name="uploadSftpFilePath">要上傳到 FTP 的路徑</param>
        /// <param name="uploadFileName">指定新檔名 (若無,預設為原本的檔名)</param>
        public void Upload(string localFilePath, string uploadSftpFilePath)
        {
            //uploadPath = setFullFileName(uploadPath, uploadFileName, localFilePath);

            using (FileStream fs = System.IO.File.OpenRead(localFilePath))
            {
                Connect();
                _sftp.UploadFile(fs, uploadSftpFilePath);
                Disconnect();
            }
        }
        public void Upload(byte[] file, string uploadSftpFilePath)
        {

            using (MemoryStream ms = new MemoryStream(file))
            {
                Connect();
                _sftp.UploadFile(ms, uploadSftpFilePath);
                Disconnect();
            }
        }

        /// <summary>
        /// SFTP 搬移檔案
        /// </summary>
        /// <param name="moveFile">要搬移的檔案</param>
        /// <param name="newPath">要搬移到的新路徑</param>
        /// <param name="newFileName">指定新檔名 (若無,預設為原本的檔名)</param>
        /// <param name="isPosix">若新路徑已有相同檔案是否要覆蓋</param>
        public void Move(string moveFile, string newPath, string newFileName = "", bool isPosix = false)
        {
            try
            {
                newPath = setFullFileName(newPath, newFileName, moveFile);
                Connect();
                _sftp.RenameFile(moveFile, newPath, isPosix);
                Disconnect();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// SFTP 下載檔案
        /// </summary>
        /// <param name="localPath">本機端路徑 (要下載到本機的路徑)</param>
        /// <param name="sftpFilePath">遠端路徑</param>
        /// <param name="localFileName">指定新檔名 (若無,預設為原本的檔名)</param>
        public void Download(string localFilePath, string sftpFilePath)
        {


            //localPath = setFullFileName(localPath, localFileName, sftpFilePath);
            Connect();
            byte[] sftp_byt = _sftp.ReadAllBytes(sftpFilePath);
            Disconnect();
            System.IO.File.WriteAllBytes(localFilePath, sftp_byt);

        }
        /// <summary>
        /// 讀取SFTP上的檔案為byte[]
        /// </summary>
        /// <param name="sftpFilePath"></param>
        /// <returns></returns>
        public byte[] ReadAllBytes(string sftpFilePath)
        {
            Connect();
            byte[] sftp_byt = _sftp.ReadAllBytes(sftpFilePath);
            Disconnect();
            return sftp_byt;

        }

        /// <summary>
        /// SFTP 刪除一個檔案
        /// </summary>
        /// <param name="deleteFilePath">要刪除的檔案</param>
        public void Delete(string deleteFilePath)
        { 
             Connect();
             _sftp.Delete(deleteFilePath);
             Disconnect(); 
        }

        /// <summary>
        /// 遞迴刪除目錄&底下所有內容
        /// </summary>
        /// <param name="directoryPath"></param>
        public void DeleteDirectoryRecursively(string directoryPath)
        {
            Connect();  
             // 列出目錄中的所有檔案和子目錄
             var filesAndDirectories = _sftp.ListDirectory(directoryPath);

            foreach (var entry in filesAndDirectories)
            {
                if (entry.Name == "." || entry.Name == "..")
                    continue;//略過

                if (entry.IsDirectory)
                {
                    // 遞迴刪除子目錄裡的檔案
                    DeleteDirectoryRecursively(entry.FullName);
                }
                else
                {
                    // 刪除檔案 
                    _sftp.Delete(entry.FullName);
                }
            }//end foreach
          
            // 刪除當前目錄 
            _sftp.DeleteDirectory(directoryPath);
            Disconnect();
        }


        /// <summary>
        /// 取得 SFTP 路徑上的檔案與文件夾
        /// </summary>
        /// <param name="sftpPath">遠端路徑</param>
        /// <param name="filenameExtension">附檔名</param>
        /// <returns></returns>
        public List<string> GetFileList(string sftpPath, string filenameExtension = "")
        {
            try
            {
                Connect();
                IEnumerable<ISftpFile> files = _sftp.ListDirectory(sftpPath);
                Disconnect();

                if (string.IsNullOrEmpty(filenameExtension))
                {
                    List<string> fileList = new List<string>();
                    foreach (var file in files)
                    {
                        string fileName = Path.GetFileName(file.FullName);
                        if (fileName == "." || fileName == "..")
                        {
                            continue;
                        }
                        fileList.Add(fileName);
                    }

                    return fileList;
                }
                else
                {
                    return matchFileName(filenameExtension, files);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 設定路徑與檔名
        /// </summary>
        /// <param name="path">路徑</param>
        /// <param name="fileName">檔名</param>
        /// <param name="sourceFullFileName">來源路徑與檔名</param>
        private string setFullFileName(string path, string fileName, string sourceFullFileName)
        {
            fileName = string.IsNullOrEmpty(fileName) ? Path.GetFileName(sourceFullFileName) : fileName;
            path += fileName;
            return path;
        }

        /// <summary>
        /// 配對檔名條件 (e.g. file_*.cs*)
        /// </summary>
        /// <param name="filenameExtension"></param>
        /// <param name="files"></param>
        private List<string> matchFileName(string filenameExtension, IEnumerable<ISftpFile> files)
        {
            string[] splitFileNameArry = filenameExtension.Split('.');
            string filterFileName = "";
            string filterExtension = splitFileNameArry[splitFileNameArry.Length - 1];
            for (int i = 0; i < splitFileNameArry.Length - 1; i++)
            {
                filterFileName += splitFileNameArry[i] + ((splitFileNameArry.Length - 2 == i) ? "" : @"\.");
            }

            string fileNamePattern = "^" + filterFileName.Replace("*", ".*") + @"$";
            string extensionPattern = @"\." + filterExtension.Replace("*", ".*") + @"$";

            List<string> fileList = new List<string>();
            foreach (var file in files)
            {
                string fileName = Path.GetFileNameWithoutExtension(file.FullName);
                string extension = Path.GetExtension(file.FullName);
                bool isMatchFileName = Regex.IsMatch(fileName, fileNamePattern);
                bool isMatchExtension = Regex.IsMatch(extension, extensionPattern);
                if (isMatchFileName && isMatchExtension)
                {
                    fileList.Add(fileName + extension);
                }
            }

            return fileList;
        }
    }
}