C# .netCore 上傳檔案到ftps/ftp
阿新 • • 發佈:2022-05-06
最近由於專案安全需要,將之前的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 }
好,到這裡踩坑完畢