近幾年來,網站全面改用 HTTPS 加密傳輸已是業界共識,違者會被 Google 調降搜尋結果排名,被 Chrome 在額頭刺上【不安全】。同樣的,檔案傳輸協定 FTP 如不升級成加密版 SFTP,也一樣會被資安單位盯上要求改善。過去寫過 C# FTP 上傳下載範例,如今也該與時俱進,研究一下 C# 如何 SFTP 下載上傳。

https://www.sftp.net/ 這個網站蒐集鉅細靡遣的 SFTP 資訊,是學習使用 SFTP 的絕佳登山口。.NET 已有不少現成的 SFTP 客戶端程式庫,包含 SSH.NET、SharpSSH、WinSCP Library、Rebex File Transfer Pack (商用軟體)、Chikat SFTP Library... 等,評估之後,我選擇最多人用的開源專案 SSH.NET,它支援範圍廣泛,通吃 .NET 3.5、.NET Core、UWP、Silverlight、Windows Phone (時代的眼淚呀...),學一套吃天下:

  • .NET Framework 3.5
  • .NET Framework 4.0 (and higher)
  • .NET Standard 1.3
  • Silverlight 4
  • Silverlight 5
  • Windows Phone 7.1
  • Windows Phone 8.0
  • Universal Windows Platform 10

老樣子,用 NuGet 查詢 ssh.net 安裝到專案就可開打:

要跑測試的話,網路上有免費的公開 SFTP Server,但都是唯讀只能測試下載。所幸,有很多免費 SFTP Server,我挑了最簡便的 Buru Server,下載 FREE 版(限個人及學術用)解壓縮,兩行指令就搞定:(第一次執行會顯示找不到 keys 目錄錯誤,但馬上會自動產生金鑰,不用擔心)

buru user add demo --rootdir D:\SFtpRoot --password demo
buru run

以下是個簡單範例,包含登入、建立資料夾、上傳檔案、將檔案搬到不同資料夾、刪除檔案、刪除目錄等各程序,應可涵蓋大部分應用 SFTP 的場景:

using Renci.SshNet;
using Renci.SshNet.Sftp;
using System;
using System.IO;
using System.Linq;
using System.Text;

namespace SFTPTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //實務應用時,帳號密碼應加密儲存於設定檔
            using (var sftp = new SftpClient("127.0.0.1", "demo", "demo"))
            {
                sftp.Connect();

                Action<SftpFile> ShowDirOrFile = (item) => {
                    if (item.IsDirectory)
                        Console.WriteLine($"[{item.Name}]");
                    else //左靠右靠對齊 https://blog.darkthread.net/blog/string-format-alignment/
                        Console.WriteLine($"{item.Name:-32} {item.Length,8:N0} bytes");
                };

                Action<string> Dir = (path) =>
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine($"Directory of {path}: \n");
                    var list = sftp.ListDirectory(path)
                        //忽略 . 及 .. 目錄
                        .Where(o => !".,..".Split(',').Contains(o.Name))
                        .ToList();
                    if (list.Any())
                        list.ForEach(ShowDirOrFile);
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.DarkYellow;
                        Console.WriteLine("No files.");
                    }
                    Console.WriteLine();
                    Console.ForegroundColor = ConsoleColor.White;
                };

                Action<string> WriteTitle = (title) =>
                {
                    Console.ForegroundColor = ConsoleColor.Cyan;
                    Console.WriteLine();
                    Console.WriteLine(title);
                    Console.WriteLine();
                };

                Dir("/");

                WriteTitle("Create folder");

                var rndName = DateTime.Now.ToString("yyyy-MM-dd");
                sftp.CreateDirectory($"/{rndName}A");

                Dir("/");

                WriteTitle("Upload file");

                using (var ms = new MemoryStream())
                {
                    var buff = Encoding.UTF8.GetBytes("Hello, World!");
                    ms.Write(buff, 0, buff.Length);
                    ms.Position = 0;
                    sftp.UploadFile(ms, $"/{rndName}A/test.txt");
                }

                Dir($"/{rndName}A");

                using (var file = new FileStream("D:\\test.txt", FileMode.Create))
                {
                    sftp.DownloadFile($"/{rndName}A/test.txt", file);
                }

                WriteTitle("Downloaded content=" + System.IO.File.ReadAllText("d:\\test.txt"));

                WriteTitle("Move file");

                sftp.CreateDirectory($"/{rndName}B");
                sftp.RenameFile($"/{rndName}A/test.txt", $"/{rndName}B/test.txt");

                Dir($"/{rndName}A");
                Dir($"/{rndName}B");

                WriteTitle("Delete file");

                sftp.DeleteFile($"/{rndName}B/test.txt");

                Dir($"/{rndName}B");

                sftp.DeleteDirectory($"/{rndName}A");
                sftp.DeleteDirectory($"/{rndName}B");
            }

            Console.Read();
        }
    }
}

實測成功!

Example of using C# to create directory, upload file, move file, delete file and delete directory.


Comments

Be the first to post a comment

Post a comment