1. 程式人生 > 其它 >C# .netCore 上傳檔案到ftps/ftp

C# .netCore 上傳檔案到ftps/ftp

最近由於專案安全需要,將之前的ftp上傳檔案的方式,改用ftps

因為不太瞭解這個東西便開始了踩坑之旅

 

首先,最近在ubuntu 上搭建了這個服務

流程可以參考這些部落格(部署網上的資源很多)

https://www.jianshu.com/p/f666278dc3b7

https://www.jianshu.com/p/413ac3ab26a3

https://blog.csdn.net/hdxyzlh_0225/article/details/50923185

ftps呢是在ftp的服務的前提上加上了 證書,保證在傳輸的過程中資料不是明文,防止被抓包洩露敏感資訊

部署的流程也在是一個完整的ftp服務上再去加證書

 

然後我這邊把我伺服器上的配置拉一下給大家參考一下

我這邊配置的 ssl 隱式連線  

# Example config file /etc/vsftpd.conf
#
# The default compiled in settings are fairly paranoid. This sample file
# loosens things up a bit, to make the ftp daemon more usable.
# Please see vsftpd.conf.5 for all compiled in defaults.
#
# READ THIS: This example file is NOT an exhaustive list of vsftpd options.
# Please read the vsftpd.conf.5 manual page to get a full idea of vsftpd's # capabilities. # # # Run standalone? vsftpd can run either from an inetd or as a standalone # daemon started from an initscript. listen=NO # # This directive enables listening on IPv6 sockets. By default, listening # on the IPv6 "any" address (::) will accept connections from both IPv6
# and IPv4 clients. It is not necessary to listen on *both* IPv4 and IPv6 # sockets. If you want that (perhaps because you want to listen on specific # addresses) then you must run two copies of vsftpd with two configuration # files. listen_ipv6=YES # # Allow anonymous FTP? (Disabled by default). anonymous_enable=NO # # Uncomment this to allow local users to log in. local_enable=YES # # Uncomment this to enable any form of FTP write command. write_enable=YES # # Default umask for local users is 077. You may wish to change this to 022, # if your users expect that (022 is used by most other ftpd's) #local_umask=022 # # Uncomment this to allow the anonymous FTP user to upload files. This only # has an effect if the above global write enable is activated. Also, you will # obviously need to create a directory writable by the FTP user. #anon_upload_enable=YES # # Uncomment this if you want the anonymous FTP user to be able to create # new directories. #anon_mkdir_write_enable=YES # # Activate directory messages - messages given to remote users when they # go into a certain directory. dirmessage_enable=YES # # If enabled, vsftpd will display directory listings with the time # in your local time zone. The default is to display GMT. The # times returned by the MDTM FTP command are also affected by this # option. use_localtime=YES # # Activate logging of uploads/downloads. xferlog_enable=YES # # Make sure PORT transfer connections originate from port 20 (ftp-data). connect_from_port_20=YES # # If you want, you can arrange for uploaded anonymous files to be owned by # a different user. Note! Using "root" for uploaded files is not # recommended! #chown_uploads=YES #chown_username=whoever # # You may override where the log file goes if you like. The default is shown # below. #xferlog_file=/var/log/vsftpd.log # # If you want, you can have your log file in standard ftpd xferlog format. # Note that the default log file location is /var/log/xferlog in this case. #xferlog_std_format=YES # # You may change the default value for timing out an idle session. #idle_session_timeout=600 # # You may change the default value for timing out a data connection. #data_connection_timeout=120 # # It is recommended that you define on your system a unique user which the # ftp server can use as a totally isolated and unprivileged user. #nopriv_user=ftpsecure # # Enable this and the server will recognise asynchronous ABOR requests. Not # recommended for security (the code is non-trivial). Not enabling it, # however, may confuse older FTP clients. #async_abor_enable=YES # # By default the server will pretend to allow ASCII mode but in fact ignore # the request. Turn on the below options to have the server actually do ASCII # mangling on files when in ASCII mode. # Beware that on some FTP servers, ASCII support allows a denial of service # attack (DoS) via the command "SIZE /big/file" in ASCII mode. vsftpd # predicted this attack and has always been safe, reporting the size of the # raw file. # ASCII mangling is a horrible feature of the protocol. #ascii_upload_enable=YES #ascii_download_enable=YES # # You may fully customise the login banner string: #ftpd_banner=Welcome to blah FTP service. # # You may specify a file of disallowed anonymous e-mail addresses. Apparently # useful for combatting certain DoS attacks. #deny_email_enable=YES # (default follows) #banned_email_file=/etc/vsftpd.banned_emails # # You may restrict local users to their home directories. See the FAQ for # the possible risks in this before using chroot_local_user or # chroot_list_enable below. #chroot_local_user=YES # # You may specify an explicit list of local users to chroot() to their home # directory. If chroot_local_user is YES, then this list becomes a list of # users to NOT chroot(). # (Warning! chroot'ing can be very dangerous. If using chroot, make sure that # the user does not have write access to the top level directory within the # chroot) #chroot_local_user=YES #chroot_list_enable=YES # (default follows) #chroot_list_file=/etc/vsftpd.chroot_list # # You may activate the "-R" option to the builtin ls. This is disabled by # default to avoid remote users being able to cause excessive I/O on large # sites. However, some broken FTP clients such as "ncftp" and "mirror" assume # the presence of the "-R" option, so there is a strong case for enabling it. #ls_recurse_enable=YES # # Customization # # Some of vsftpd's settings don't fit the filesystem layout by # default. # # This option should be the name of a directory which is empty. Also, the # directory should not be writable by the ftp user. This directory is used # as a secure chroot() jail at times vsftpd does not require filesystem # access. secure_chroot_dir=/var/run/vsftpd/empty # # This string is the name of the PAM service vsftpd will use. pam_service_name=vsftpd # # This option specifies the location of the RSA certificate to use for SSL # encrypted connections. #rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem #rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key ssl_enable=YES ssl_tlsv1=YES ssl_sslv2=NO ssl_sslv3=NO allow_anon_ssl=NO force_local_data_ssl=YES force_local_logins_ssl=YES listen_port=10012 rsa_cert_file=/etc/ssl/private/vsftpd.pem rsa_private_key_file=/etc/ssl/private/vsftpd.pem implicit_ssl=YES connect_from_port_20=NO debug_ssl=YES local_root=/home/uftp/testFiles # # Uncomment this to indicate that vsftpd use a utf8 filesystem. #utf8_filesystem=YES

 

這個是FileZilla 連線工具的設定()

 

 

 

 

 

 

 

配置完後,工具能正常連線上,現在開始上程式碼

// See https://aka.ms/new-console-template for more information
using FluentFTP;
using System.Net;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;

Console.WriteLine("Hello, World!");

string path = @"C:\Users\36493\Desktop\Test\UploadFileFtps\UploadFileFtps\2022-04-29182023.xml";

//建立目錄
UploadToFtpFileHelper.CreateFileContent("2021/05/06/test");

//下載檔案
//UploadToFtpFileHelper.DownloadFile(@"C:\Users\36493\Desktop\test002\111.txt", @"111.txt");

//上傳檔案
UploadToFtpFileHelper.UploadFile(path, @"2021/05/06/test/2022-04-29182023.xml");
public static class UploadToFtpFileHelper
{
    //普通的ftp
    private static string ftp_postUrl = "ftp://xx.xxx.x.xxx/";
    private static string ftp_user = "xxxxx";
    private static string ftp_pwd = "xxxxx";


    //使用ssl通訊加密的方式連線ftp
    private static string ftps_postUrl = "xx.xxx.x.xxx";
    private static string ftps_user = "xxx";
    private static string ftps_pwd = "xxxx";
    private static int ftps_port = xx;


    #region Ftps

    private static FtpClient ftpClient = null;

    private static async Task<FtpClient> ConnectionFtp()
    {
        // 建立 FTP client
        FtpClient client = new FtpClient(ftps_postUrl);
        // 如果您不指定登入憑證,我們將使用"anonymous"使用者帳戶
        client.Credentials = new NetworkCredential(ftps_user, ftps_pwd);
        client.Port = ftps_port;
        //選擇證書連線的方式 TLS
        client.SslProtocols = System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Ssl2 | System.Security.Authentication.SslProtocols.Ssl3;
        client.Encoding = System.Text.Encoding.UTF8;
        client.EncryptionMode = FtpEncryptionMode.Explicit;//ssl連結錯誤
        client.ValidateCertificate += new FtpSslValidation(OnValidateCertificate);//證書驗證
        void OnValidateCertificate(FtpClient control, FtpSslValidationEventArgs e)
        {
            //在這裡新增邏輯以測試證書是否有效
            e.Accept = true;
        }
        var connect = client.AutoConnect();//根據驗證過程,遠端證書無效
        if (!client.IsConnected)
        {
            Console.WriteLine("連線建立失敗");
            throw new Exception("連線建立失敗");
        }
        return client;
    }


    /// <summary>
    /// 從ftp下載檔案(ssl)
    /// </summary>
    /// <param name="localPath">要下載到本地哪兒</param>
    /// <param name="ftpFilePath">ftp的檔案路徑</param>
    public static async Task<FtpStatus> DownloadFile(string localPath, string ftpFilePath)
    {
        ftpClient = ftpClient == null ? await ConnectionFtp() : ftpClient;
        FtpStatus status = ftpClient.DownloadFile(localPath, ftpFilePath);
        //ftpClient.Disconnect();
        return status;
    }

    /// <summary>
    /// 上傳檔案到ftp(ssl)
    /// </summary>
    /// <param name="localPath">本地的檔案路徑,完整的</param>
    /// <param name="ftpFilePath">要上傳到的地址</param>
    /// <returns></returns>
    public static async Task<FtpStatus> UploadFile(string localPath, string ftpFilePath)
    {
        ftpClient = ftpClient == null ? await ConnectionFtp() : ftpClient;
        FtpStatus status = ftpClient.UploadFile(localPath, ftpFilePath);
        //ftpClient.Disconnect();
        return status;
    }


    /// <summary>
    /// 建立檔案目錄
    /// </summary>
    public static async Task<bool> CreateFileContent(string createPath)
    {
        ftpClient = ftpClient == null ? await ConnectionFtp() : ftpClient;
        bool result = ftpClient.CreateDirectory(createPath);
        //ftpClient.Disconnect();
        return result;
    }



    public static void Mains()
    {
        // 建立 FTP client
        FtpClient client = new FtpClient("10.168.1.128");
        // 如果您不指定登入憑證,我們將使用"anonymous"使用者帳戶
        client.Credentials = new NetworkCredential("tgy", "test123456");
        client.Port = 10012;
        //選擇證書連線的方式 TLS
        client.SslProtocols = System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Ssl2 | System.Security.Authentication.SslProtocols.Ssl3;
        client.Encoding = System.Text.Encoding.UTF8;
        client.EncryptionMode = FtpEncryptionMode.Explicit;//ssl連結錯誤
        client.ValidateCertificate += new FtpSslValidation(OnValidateCertificate);//證書驗證
        void OnValidateCertificate(FtpClient control, FtpSslValidationEventArgs e)
        {
            //在這裡新增邏輯以測試證書是否有效
            e.Accept = true;
        }

        //開始連線Server
        try
        {
            var connect = client.AutoConnect();//根據驗證過程,遠端證書無效
            if (!client.IsConnected)
            {
                Console.WriteLine("連線建立失敗");
            }

            foreach (FtpListItem item in client.GetListing("testFiles"))
            {
                //如果是 file
                if (item.Type == FtpFileSystemObjectType.File)
                {
                    // get the file size
                    long size = client.GetFileSize(item.FullName);
                }
                // 獲取檔案或資料夾的修改日期/時間
                DateTime time = client.GetModifiedTime(item.FullName);
                // 計算伺服器端檔案的雜湊值(預設演算法)
                //FtpHash hash = client.GetHash(item.FullName);
            }

            if (client.DirectoryExists("test"))
            {

            }

            var gg = client.DownloadFile(@"C:\Users\36493\Desktop\test002\111.txt", @"111.txt");

            var oo = client.UploadFile(@"C:\Users\36493\Desktop\test002\yyds.txt", "yyds.txt");
        }
        catch (Exception ex)
        {

            throw;
        }
    }





    #endregion


    #region Ftp
    /// <summary>
    /// 上傳專案指定的檔案到ftp伺服器
    /// </summary>
    /// <param name="localfilePath">服務檔案路徑</param>
    /// <param name="fileName">上傳檔案路徑及名稱(包含字尾)</param>
    /// <exception cref="Exception"></exception>
    public static async void UploadToFtp(string localfilePath, string urlfileName)//, UploadType Type
    {
        if (!File.Exists(localfilePath))
        {
            throw new FileNotFoundException("未獲取到本地伺服器檔案!");
        }

        if (!CheckDirectoryExist(urlfileName))
        {
            FtpCheckExis(urlfileName);
        }
        await UploadDetails_Ftp(localfilePath, urlfileName);
    }


    /// <summary>
    /// 把一個ftp檔案從指定路徑複製到 另一個路徑下
    /// </summary>
    /// <param name="oldfilePath">舊的ftp的url</param>
    /// <param name="newfilePath">新的ftp的url</param>
    /// <returns></returns>
    public static async Task<string> CopyFtpToFtp(string oldfilePath, string newfilePath)
    {
        return "";
    }

    private static async Task UploadDetails_Ftp(string localfilePath, string urlfileName)
    {
        FileInfo fileInfo = new FileInfo(localfilePath);
        var reqFTP = (FtpWebRequest)FtpWebRequest.Create(ftp_postUrl + urlfileName);
        reqFTP.EnableSsl = true;
        reqFTP.Credentials = new NetworkCredential(ftp_user, ftp_pwd);
        reqFTP.KeepAlive = false;//預設位true,連結不會被關閉,再一個指令之後被執行
        reqFTP.Method = WebRequestMethods.Ftp.UploadFile; //指定執行什麼命令
        reqFTP.UseBinary = true;//指定資料傳輸型別
        reqFTP.ContentLength = fileInfo.Length;//上傳檔案通知伺服器,檔案的大小
        int buffLength = 2048;//緩衝大小設定位2kb
        byte[] buff = new byte[buffLength];
        int contentlen;

        FileStream fs = fileInfo.OpenRead();
        try
        {
            Stream stream = await reqFTP.GetRequestStreamAsync();//把上傳的檔案寫入流
            contentlen = fs.Read(buff, 0, buff.Length);//每次讀取檔案的流的2kb

            while (contentlen != 0)//流內容沒有結束
            {
                stream.Write(buff, 0, contentlen);
                contentlen = fs.Read(buff, 0, buff.Length);
            }
            stream.Close();
            fs.Close();
        }
        catch (Exception ex)
        {
        }
    }


    /// <summary>
    /// 判斷ftp伺服器上是否存在該目錄
    /// </summary>
    /// <param name="ftpPath">ftp路徑目錄</param>
    /// <param name="dirName">目錄上的資料夾名稱</param>
    /// <returns></returns>
    private static bool CheckDirectoryExist(string dirName)
    {
        bool flag = true;
        try
        {
            var reqFTP = (FtpWebRequest)FtpWebRequest.Create(ftp_postUrl + dirName);
            reqFTP.Credentials = new NetworkCredential(ftp_user, ftp_pwd);
            reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;

            FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
            response.Close();
        }
        catch (Exception ex)
        {
            flag = false;
        }
        return flag;
    }


    /// <summary>
    /// 建立資料夾
    /// </summary>
    /// <param name="ftpPath">ftp路徑</param>
    /// <param name="dirName">建立資料夾名稱</param>
    private static async Task MakerDir(string dirName)
    {
        FtpWebRequest reqFtp;
        try
        {
            reqFtp = (FtpWebRequest)FtpWebRequest.Create((ftp_postUrl + dirName).Trim());
            reqFtp.Method = WebRequestMethods.Ftp.MakeDirectory;
            reqFtp.UseBinary = true;
            reqFtp.Credentials = new NetworkCredential(ftp_user, ftp_pwd);
            FtpWebResponse resp = (FtpWebResponse)reqFtp.GetResponse();
            Stream ftpStream = resp.GetResponseStream();
            ftpStream.Close();
            resp.Close();
        }
        catch (Exception ex)
        {
        }
    }


    /// <summary>
    /// 判斷檔案的目錄是否存在
    /// </summary>
    public static void FtpCheckExis(string urlFilepath)
    {
        string fullDir = urlFilepath.Substring(0, urlFilepath.LastIndexOf("/"));
        string[] dirs = fullDir.Split('/');
        string curDir = "/";
        for (int i = 0; i < dirs.Length; i++)
        {
            string dir = dirs[i];
            if (dir != null && dir.Length > 0)
            {
                try
                {
                    curDir += dir + "/";
                    FtpMakerDir(curDir);
                }
                catch (Exception)
                {
                }
            }
        }
    }


    /// <summary>
    /// 新建檔案目錄
    /// </summary>
    /// <param name="file"></param>
    /// <returns></returns>
    public static bool FtpMakerDir(string file)
    {
        FtpWebRequest req = (FtpWebRequest)WebRequest.Create(ftp_postUrl + file);
        req.Credentials = new NetworkCredential(ftp_user, ftp_pwd);
        req.Method = WebRequestMethods.Ftp.MakeDirectory;
        try
        {
            FtpWebResponse request = (FtpWebResponse)req.GetResponse();
            request.Close();
        }
        catch (Exception)
        {
            req.Abort();
            return false;
        }
        req.Abort();
        return true;
    }
    #endregion

}

 

好,到這裡踩坑完畢