白码一号的博客园

最近由于项目安全需要,将之前的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 连接工具的设置()

 

 

 

 

 

 

 

配置完后,工具能正常连接上,现在开始上代码  FluentFTP用的版本是 FluentFTP, Version=37.0.2.0  最新的版本代码有变化

// 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

}

 

好,到这里踩坑完毕
经过项目复盘优化

性能更高、更快速的ftps的连接方式如下

public async Task<FtpClient> Connection(FtpConfigure ftpConfigure)
        {
            // 创建 FTP client
            Temporaryclient = new FtpClient(ftpConfigure.ftps_Url);
            // 如果您不指定登录凭证,我们将使用"anonymous"用户帐户
            Temporaryclient.Credentials = new NetworkCredential(ftpConfigure.ftps_user, ftpConfigure.ftps_pwd);
            Temporaryclient.Port = ftpConfigure.ftps_port;
            //选择证书连接的方式 TLS
            Temporaryclient.SslProtocols = System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13;
            Temporaryclient.Encoding = System.Text.Encoding.UTF8;
            Temporaryclient.EncryptionMode = FtpEncryptionMode.Implicit;//ssl链接错误
            Temporaryclient.DataConnectionType = FtpDataConnectionType.PASV;
            Temporaryclient.ValidateCertificate += new FtpSslValidation(OnValidateCertificate);//证书验证
            void OnValidateCertificate(FtpClient control, FtpSslValidationEventArgs e)
            {
                //在这里添加逻辑以测试证书是否有效
                e.Accept = true;
            }
            //var connect = Temporaryclient.AutoConnect();//根据验证过程,远程证书无效
            Temporaryclient.Connect();
            if (!Temporaryclient.IsConnected)
            {
                Console.WriteLine("上级ftps连接建立失败");
                _logger.LogError("上级ftps连接建立失败");
            }
            return Temporaryclient;
        }

如果遇到并发传输记得枷锁哦

public async Task<FtpStatus> DownloadFile(string localPath, string ftpFilePath, SystemType type)
        {
            FtpStatus status = FtpStatus.Success;
            try
            {              
                lock (_locker)
                {
                    SelectConnection(type);
                    if (!Publicclient.IsConnected)
                    {
                        _logger.LogError("ftp连接失败");
                    }

                    status = Publicclient.DownloadFile(localPath, ftpFilePath);
                }             
                //ftpClient.Disconnect();
            }
            catch (Exception ex)
            {
                _logger.LogError("请求失败,原因:"+ex);
            }
            return status;
        }

 上面服务连接较慢 继续更新继续优化

using FluentFTP;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Service.IntelligentRemotePatrol.Application.Contracts.Common;
using Service.IntelligentRemotePatrol.ConfigOptions;
using Service.IntelligentRemotePatrol.Enums;
using Service.IntelligentRemotePatrol.Helper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Modularity;

namespace Service.IntelligentRemotePatrol.Application.Common
{

    public class UploadToFtpService : IUpLoadFtpFileService
    {
        private object _lockerArest = new object();
        private object _locker = new object();
        private object _locker_that = new object();

        private readonly ILogger<UploadToFtpService> _logger;

        public UploadToFtpService(ServiceConfigurationContext context, ILogger<UploadToFtpService> logger)
        {
            _logger = logger;

            #region ftps
            var _appConfig = context.Services.BuildServiceProvider().GetService<IOptions<AppConfig>>()?.Value ??
                         new AppConfig();
            List<FtpConfigure> ftpConfigureList = new List<FtpConfigure>();
            FtpConfigure SuperiorFtpConfigure1 = new FtpConfigure
            {
                ftps_port = string.IsNullOrEmpty(_appConfig.FTPSuperiorSettings.Port) ? 0 : Convert.ToInt32(_appConfig.FTPSuperiorSettings.Port),
                ftps_pwd = _appConfig.FTPSuperiorSettings.Pwd,
                ftps_user = _appConfig.FTPSuperiorSettings.Account,
                ftps_Url = _appConfig.FTPSuperiorSettings.Ip,
                Type = SystemType.Superior,
            };
            FtpConfigure AreaFtpConfigure1 = new FtpConfigure
            {
                ftps_port = string.IsNullOrEmpty(_appConfig.FTPAreaSettings.Port) ? 0 : Convert.ToInt32(_appConfig.FTPAreaSettings.Port),
                ftps_pwd = _appConfig.FTPAreaSettings.Pwd,
                ftps_user = _appConfig.FTPAreaSettings.Account,
                ftps_Url = _appConfig.FTPAreaSettings.Ip,
                Type = SystemType.Area,
            };
            FtpConfigure EdgeFtpConfigure1 = new FtpConfigure
            {
                ftps_port = string.IsNullOrEmpty(_appConfig.FTPEdgeSettings.Port) ? 0 : Convert.ToInt32(_appConfig.FTPEdgeSettings.Port),
                ftps_pwd = _appConfig.FTPEdgeSettings.Pwd,
                ftps_user = _appConfig.FTPEdgeSettings.Account,
                ftps_Url = _appConfig.FTPEdgeSettings.Ip,
                Type = SystemType.Edge,
            };
            if (!string.IsNullOrEmpty(SuperiorFtpConfigure1.ftps_Url))
            {
                ftpConfigureList.Add(SuperiorFtpConfigure1);
            }
            if (!string.IsNullOrEmpty(AreaFtpConfigure1.ftps_Url))
            {
                ftpConfigureList.Add(AreaFtpConfigure1);
            }
            if (!string.IsNullOrEmpty(EdgeFtpConfigure1.ftps_Url))
            {
                ftpConfigureList.Add(EdgeFtpConfigure1);
            }
            Task.Run(() => ConnectionFtp(ftpConfigureList)).Wait();
            //ftps
            #endregion
        }


        /// <summary>
        /// 临时
        /// </summary>
        private FtpClient Temporaryclient = null;

        /// <summary>
        /// 上级的ftps
        /// </summary>
        private FtpClient Superiorclient = null;

        /// <summary>
        /// 区域的ftps
        /// </summary>
        private FtpClient Areaclient = null;

        /// <summary>
        /// 边缘的ftps
        /// </summary>
        private FtpClient Edgeclient = null;

        /// <summary>
        /// 公共的
        /// </summary>
        private FtpClient Publicclient = null;

        public async void ConnectionFtp(List<FtpConfigure> ftpConfigures)
        {
            foreach (var item in ftpConfigures)
            {
                try
                {
                    switch (item.Type)
                    {
                        case SystemType.Superior:
                            Superiorclient = await Connection(item);
                            break;
                        case SystemType.Area:
                            Areaclient = await Connection(item);
                            break;
                        case SystemType.Edge:
                            Edgeclient = await Connection(item);
                            break;
                        default:
                            break;
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "ftps连接建立异常。");
                }
            }
        }


        public async Task<FtpClient> Connection(FtpConfigure ftpConfigure)
        {
            // 创建 FTP client
            Temporaryclient = new FtpClient(ftpConfigure.ftps_Url);
            // 如果您不指定登录凭证,我们将使用"anonymous"用户帐户
            Temporaryclient.Credentials = new NetworkCredential(ftpConfigure.ftps_user, ftpConfigure.ftps_pwd);
            Temporaryclient.Port = ftpConfigure.ftps_port;
            //选择证书连接的方式 TLS
            //Temporaryclient.SslProtocols = System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13;
            Temporaryclient.Encoding = System.Text.Encoding.UTF8;
            Temporaryclient.ValidateCertificate += Temporaryclient_ValidateCertificate;

            Temporaryclient.Config.EncryptionMode = FtpEncryptionMode.Implicit;
            Temporaryclient.Config.DataConnectionType = FtpDataConnectionType.PASV;
            Temporaryclient.Config.SocketKeepAlive = true;
            Temporaryclient.Config.SslProtocols = System.Security.Authentication.SslProtocols.Tls
                                | SslProtocols.Tls11
                                | SslProtocols.Tls12
                                | SslProtocols.Tls13;

            //var connect = Temporaryclient.AutoConnect();//根据验证过程,远程证书无效
            Temporaryclient.Connect();
            if (!Temporaryclient.IsConnected)
            {
                Console.WriteLine("上级ftps连接建立失败");
                _logger.LogError("上级ftps连接建立失败");
            }
            return Temporaryclient;
        }

        private void Temporaryclient_ValidateCertificate(FluentFTP.Client.BaseClient.BaseFtpClient control, FtpSslValidationEventArgs e)
        {
            e.Accept = true;
        }


        /// <summary>
        /// 从ftp下载文件(ssl) 
        /// </summary>
        /// <param name="localPath">要下载到本地哪儿</param>
        /// <param name="ftpFilePath">ftp的文件路径</param>
        /// <param name="type">//1上级 2区域 3边缘</param>
        public async Task<FtpStatus> DownloadFile(string localPath, string ftpFilePath, SystemType type)
        {
            FtpStatus status = FtpStatus.Success;
            try
            {
                lock (_locker)
                {
                    SelectConnection(type);
                    if (!Publicclient.IsConnected)
                    {
                        _logger.LogError("ftp连接失败");
                    }

                    status = Publicclient.DownloadFile(localPath, ftpFilePath);
                }
                //ftpClient.Disconnect();
            }
            catch (Exception ex)
            {
                _logger.LogError("请求失败,原因:" + ex);
            }
            return status;
        }

        /// <summary>
        /// 从ftp下载文件(ssl) 下载目录下的所有文件
        /// </summary>
        /// <param name="localPath">要下载到本地哪儿</param>
        /// <param name="ftpFilePath">ftp的文件路径</param>
        /// <param name="type">//1上级 2区域 3边缘</param>
        public async Task<MemoryStream> Download(string ftpFilePath, SystemType type, MemoryStream stream)
        {
            MemoryStream memoryStream = new MemoryStream();
            try
            {
                lock (_locker)
                {
                    SelectConnection(type);

                    //var exist = Publicclient.FileExists(ftpFilePath);
                    //_logger.LogInformation($"ft当前路径:{ftpFilePath} 文件是否存在:{exist}");

                    //var size = Publicclient.GetFileSize(ftpFilePath);
                    //_logger.LogInformation($"ft当前路径:{ftpFilePath} 文件大小:{size}");

                    Publicclient.DownloadStream(memoryStream, ftpFilePath);

                    //ftpClient.Disconnect();                   
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"ftp下载图失败,当前路径:{ftpFilePath}原因:" + ex.Message);
            }
            return memoryStream;
        }

        public async Task<FtpListItem[]> GetListing(string url, SystemType type)
        {
            SelectConnection(type);
            return Publicclient.GetListing(url);
        }


        /// <summary>
        /// 上传Http文件到ftp(ssl)  先下载http的文件地址的图片再上传到指定ftp路径
        /// </summary>
        /// <param name="localPath"></param>
        /// <param name="ftpFilePath"></param>
        /// <param name="type">//1上级 2区域 3边缘</param>
        /// <returns></returns>
        public async Task<FtpStatus> ConnectionFtpHttp(List<string> fileurl, string ftpFilePath, SystemType type)
        {
            SelectConnection(type);
            FtpStatus status = FtpStatus.Success;
            try
            {
                if (ftpFilePath.Substring(ftpFilePath.Length - 1, 1) == "/")
                {
                    CheckThePathExists(ftpFilePath);
                }
                else
                {
                    if (ftpFilePath.Contains("/"))
                    {
                        var path = ftpFilePath.Substring(0, ftpFilePath.LastIndexOf("/") + 1);
                        CheckThePathExists(path);
                    }
                }

                foreach (var item in fileurl)
                {
                    //string fileName = string.Empty;
                    //if (item.Contains("/"))
                    //{
                    //    fileName = item.Split("/")[item.Split("/").Length - 1];
                    //}
                    _logger.LogInformation("下载地图文件的url为:" + item);
                    status = Publicclient.UploadBytes(RestClientHelper.DownloadData(item), ftpFilePath);
                }
                //ftpClient.Disconnect();              
            }
            catch (Exception ex)
            {
                throw new UserFriendlyException(ex.Message);
            }
            return status;
        }

        /// <summary>
        /// 上传Http文件到ftp(ssl)  先下载http的文件地址的图片再上传到指定ftp路径
        /// </summary>
        /// <param name="fileurl">文件地址集合</param>
        /// <param name="fileNames">文件名称集合,与文件地址一对一,如果不传,则自动默认取文件路径上的文件名称</param>
        /// <param name="ftpFilePath">要存储的文件路径</param>
        /// <param name="type">//1上级 2区域 3边缘</param>
        /// <returns></returns>
        public async Task<FtpStatus> ConnectionFtpHttpList(List<string> fileurl, string ftpFilePath, SystemType type, List<string> fileNames)
        {
            SelectConnection(type);
            FtpStatus status = FtpStatus.Success;
            try
            {
                if (ftpFilePath.Substring(ftpFilePath.Length - 1, 1) == "/")
                {
                    CheckThePathExists(ftpFilePath);
                }
                else
                {
                    if (ftpFilePath.Contains("/"))
                    {
                        var path = ftpFilePath.Substring(0, ftpFilePath.LastIndexOf("/") + 1);
                        CheckThePathExists(path);
                    }
                }
                string fileName = string.Empty;
                for (int i = 0; i < fileurl.Count; i++)
                {
                    if (fileNames != null && fileNames.Count > 0 && fileNames.Count > i && !string.IsNullOrWhiteSpace(fileNames[i]))
                    {
                        fileName = fileNames[i];
                    }
                    else
                    {
                        if (fileurl[i].Contains("/"))
                        {
                            fileName = fileurl[i].Split("/")[fileurl[i].Split("/").Length - 1];
                        }
                        else
                        {
                            fileName = DateTime.Now.ToString("yyyyMMddHHmmssfff") + fileurl[i].Split(".")[fileurl[i].Split(".").Length - 1];
                        }
                    }
                    status = Publicclient.UploadBytes(RestClientHelper.DownloadData(fileurl[i]), ftpFilePath + fileName);

                }
                //ftpClient.Disconnect();              
            }
            catch (Exception ex)
            {
                throw new UserFriendlyException(ex.Message);
            }
            return status;
        }

        /// <summary>
        /// 上传本地文件到ftp服务器
        /// </summary>
        /// <param name="filesDic">文件名为key,全路径为value</param>
        /// <param name="ftpFilePath"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public async Task<FtpStatus> UploadLocalFiles(Dictionary<string, string> filesDic, string ftpFilePath, SystemType type)
        {
            SelectConnection(type);
            FtpStatus status = FtpStatus.Success;

            try
            {
                if (ftpFilePath.Substring(ftpFilePath.Length - 1, 1) == "/")
                {
                    CheckThePathExists(ftpFilePath);
                }
                else
                {
                    if (ftpFilePath.Contains("/"))
                    {
                        var path = ftpFilePath.Substring(0, ftpFilePath.LastIndexOf("/") + 1);
                        CheckThePathExists(path);
                    }
                }
                foreach (var item in filesDic)
                {
                    status = Publicclient.UploadFile(item.Value, ftpFilePath + item.Key);//目前就一个文件,status就这样了先
                }

            }
            catch (Exception ex)
            {
                throw;
            }
            return status;
        }

        /// <summary>
        /// 上传Btye[]文件到ftp(ssl)  
        /// </summary>
        /// <param name="fileurl">图片数组</param>
        /// <param name="ftpFilePath">要保存的路径</param>
        /// <param name="fileName">文件名称</param>
        /// <param name="type">//1上级 2区域 3边缘</param>
        /// <returns></returns>
        public async Task<FtpStatus> ConnectionFtpByByte(byte[] fileurl, string ftpFilePath, string fileName, SystemType type)
        {

            FtpStatus status = FtpStatus.Success;
            try
            {
                lock (_locker_that)
                {
                    SelectConnection(type);
                    if (ftpFilePath.Substring(ftpFilePath.Length - 1, 1) == "/")
                    {
                        CheckThePathExists(ftpFilePath);
                    }
                    else
                    {
                        if (ftpFilePath.Contains("/"))
                        {
                            var path = ftpFilePath.Substring(0, ftpFilePath.LastIndexOf("/") + 1);
                            CheckThePathExists(path);
                        }
                    }

                    status = Publicclient.UploadBytes(fileurl, ftpFilePath + fileName);
                }

            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "图片上次至FTP服务器异常。");
                //throw new UserFriendlyException(ex.Message);
                status = FtpStatus.Failed;
            }
            return status;
        }

        /// <summary>
        /// 上传Btye[]文件到ftp(ssl)  
        /// </summary>
        /// <param name="fileurl">图片数组</param>
        /// <param name="ftpFilePath">要保存的路径</param>
        /// <param name="fileName">文件名称</param>
        /// <param name="type">//1上级 2区域 3边缘</param>
        /// <returns></returns>
        public async Task<FtpStatus> ConnectionFtpByByte(Stream fileStream, string ftpFilePath, string fileName, SystemType type)
        {
            FtpStatus status = FtpStatus.Success;
            try
            {
                lock (_locker_that)
                {
                    SelectConnection(type);
                    if (ftpFilePath.Substring(ftpFilePath.Length - 1, 1) == "/")
                    {
                        CheckThePathExists(ftpFilePath);
                    }
                    else
                    {
                        if (ftpFilePath.Contains("/"))
                        {
                            var path = ftpFilePath.Substring(0, ftpFilePath.LastIndexOf("/") + 1);
                            CheckThePathExists(path);
                        }
                    }
                    status = Publicclient.UploadStream(fileStream, ftpFilePath + fileName);
                }

            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "图片上次至FTP服务器异常。");
                //throw new UserFriendlyException(ex.Message);
                status = FtpStatus.Failed;
            }
            return status;
        }

        /// <summary>
        /// 上传文件到ftp(ssl)
        /// </summary>
        /// <param name="localPath">本地的文件路径,完整的</param>
        /// <param name="ftpFilePath">要上传到的地址</param>
        /// <param name="type">//1上级 2区域 3边缘</param>
        /// <returns></returns>
        public async Task<FtpStatus> UploadFileFtps(string localPath, string ftpFilePath, SystemType type)
        {
            FtpStatus status = FtpStatus.Success;
            try
            {
                lock (_lockerArest)
                {
                    SelectConnection(type);
                    string pathStr = ftpFilePath;
                    if (ftpFilePath.Contains("/"))
                    {
                        var path = ftpFilePath.Substring(0, ftpFilePath.LastIndexOf("/") + 1);
                        CheckThePathExists(path);
                        pathStr = ftpFilePath.Substring(ftpFilePath.LastIndexOf("/") + 1);
                    }
                    status = Publicclient.UploadFile(localPath, ftpFilePath);//ftpFilePath
                    //status = Publicclient.UploadFile(@"C:\Users\36493\Desktop\测试文件\2022-10-27180030189.xml", pathStr);
                    //ftpClient.Disconnect();  
                }
            }
            catch (Exception ex)
            {
                throw new UserFriendlyException(ex.Message);
            }
            return status;
        }


        /// 2022年10/27 号出现了 文件能上传但是无文件大小的问题  持续到28号上午  问题还没找到 突然又正常了 ……………………………………………………心情复杂
        /// <summary>
        /// 上传文件到ftp(ssl)
        /// </summary>
        /// <param name="localPath">本地的文件路径,完整的</param>
        /// <param name="ftpFilePath">要上传到的地址</param>
        /// <param name="type">//1上级 2区域 3边缘</param>
        /// 用流的形式
        /// <returns></returns>
        public async Task<FtpStatus> UploadFileFtps_bug(string localPath, string ftpFilePath, SystemType type)
        {
            string name = string.Empty;
            if (!localPath.Contains('/'))
            {
                name = localPath.Split("\\")[(localPath.Split("\\").Length - 1)];
            }
            else
            {
                name = localPath;
            }
            string path = string.Empty;
            SelectConnection(type);
            FtpStatus status = FtpStatus.Success;
            try
            {
                string pathStr = ftpFilePath;
                if (ftpFilePath.Contains("/"))
                {
                    path = ftpFilePath.Substring(0, ftpFilePath.LastIndexOf("/") + 1);
                    CheckThePathExists(path);
                    pathStr = ftpFilePath.Substring(ftpFilePath.LastIndexOf("/") + 1);
                }

                // 打开文件
                FileStream fileStream = new FileStream(localPath, FileMode.Open, FileAccess.Read, FileShare.Read);
                // 读取文件的 byte[]
                // 把 byte[] 转换成 Stream
                MemoryStream stream = new MemoryStream();
                //byte[] bytes = new byte[fileStream.Length];
                //fileStream.Read(bytes, 0, bytes.Length);
                await fileStream.CopyToAsync(stream);
                //fileStream.Close();
                try
                {
                    await ConnectionFtpByByte(stream, path, name, SystemType.Area);
                    //await ConnectionFtpByByte(stream.ToArray(), path, name, SystemType.Area);
                    //await ConnectionFtpByByte(stream.ToArray(), "/", name, SystemType.Area);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "区域收到边缘上报图片,存储到ftp报错。");
                }

                //status = Publicclient.UploadFile(localPath, ftpFilePath);
                //status = Publicclient.UploadFile(localPath, pathStr);
                //ftpClient.Disconnect();              
            }
            catch (Exception ex)
            {
                throw new UserFriendlyException(ex.Message);
            }
            return status;
        }

        /// <summary>
        /// 修改文件名
        /// </summary>
        /// <param name="path"></param>
        /// <param name="name"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        public async Task<FtpStatus> Rename(string path, string name, SystemType type)
        {
            SelectConnection(type);
            FtpStatus status = FtpStatus.Success;
            try
            {
                if (!Publicclient.DirectoryExists(path))
                {
                    return FtpStatus.Success;
                }
                Publicclient.Rename(path, name);
                //ftpClient.Disconnect();              
            }
            catch (Exception ex)
            {
                throw new UserFriendlyException(ex.Message);
            }
            return status;
        }

        private void SelectConnection(SystemType type)
        {
            try
            {
                switch (type)
                {
                    case SystemType.Superior:
                        Publicclient = Superiorclient;
                        break;
                    case SystemType.Area:
                        Publicclient = Areaclient;
                        break;
                    case SystemType.Edge:
                        Publicclient = Edgeclient;
                        break;
                    default:
                        break;
                }

                Publicclient.ValidateCertificate += Publicclient_ValidateCertificate;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "验证ftps的证书异常:" + ex);
            }

        }
        /// <summary>
        /// //证书验证
        /// </summary>
        /// <param name="control"></param>
        /// <param name="e"></param>
        private void Publicclient_ValidateCertificate(FluentFTP.Client.BaseClient.BaseFtpClient control, FtpSslValidationEventArgs e)
        {
            e.Accept = true;
        }

        /// <summary>
        /// 判断路径是否存在 不在就创建
        /// </summary>
        /// <param name="ftpFilePath"></param>
        private void CheckThePathExists(string ftpFilePath)
        {
            try
            {
                Publicclient.ValidateCertificate += Publicclient_ValidateCertificate1;
                if (!Publicclient.DirectoryExists(ftpFilePath))
                {
                    var state = Publicclient.CreateDirectory(ftpFilePath);
                }
            }
            catch (Exception ex)
            {
                throw new UserFriendlyException(ex.Message);
            }
        }

        private void Publicclient_ValidateCertificate1(FluentFTP.Client.BaseClient.BaseFtpClient control, FtpSslValidationEventArgs e)
        {
            //在这里添加逻辑以测试证书是否有效
            e.Accept = true;
        }
    }
}

 

再次补充

public async Task<FtpClient> Connection(FtpConfigure ftpConfigure)
        {
            try
            {
                var ftpconfig = new FtpConfig();
                ftpconfig.ConnectTimeout = 60 * 1000;
                ftpconfig.ReadTimeout = 60 * 1000;
                ftpconfig.DataConnectionConnectTimeout = 60 * 1000;
                ftpconfig.DataConnectionReadTimeout = 60 * 1000;
                // 创建 FTP client
                var Temporaryclient = new FtpClient(ftpConfigure.ftps_Url, ftpConfigure.ftps_user, ftpConfigure.ftps_pwd, ftpConfigure.ftps_port, ftpconfig, _logger);
               
                // 如果您不指定登录凭证,我们将使用"anonymous"用户帐户               
               
                Temporaryclient.Encoding = System.Text.Encoding.UTF8;
                Temporaryclient.ValidateCertificate += Temporaryclient_ValidateCertificate;
                Temporaryclient.Config.ValidateAnyCertificate = true;
                Temporaryclient.Config.EncryptionMode = FtpEncryptionMode.Implicit;
                Temporaryclient.Config.DataConnectionType = FtpDataConnectionType.PASV;
                Temporaryclient.Config.SocketKeepAlive = true;
                Temporaryclient.Config.SslProtocols = System.Security.Authentication.SslProtocols.Tls
                                    | SslProtocols.Tls11
                                    | SslProtocols.Tls12
                                    | SslProtocols.Tls13;
                //Temporaryclient.Config.SslProtocols = SslProtocols.Tls13;

                //var connect = Temporaryclient.AutoConnect();//根据验证过程,远程证书无效
                Temporaryclient.Connect();
                if (!Temporaryclient.IsConnected)
                {
                    Console.WriteLine("连接建立失败");
                    _logger.LogError("连接建立失败:" + ftpConfigure.ToJson());
                }
                _logger.LogInformation($"ftps连接成功IP:{ftpConfigure.ftps_Url}");
                return Temporaryclient;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "连接建立失败" + ftpConfigure.ToJson());
            }
            return Temporaryclient;
        }

        private void Temporaryclient_ValidateCertificate(FluentFTP.Client.BaseClient.BaseFtpClient control, FtpSslValidationEventArgs e)
        {
            e.Accept = true;
        }

补充注入到服务中: context.Services.AddFtpClient(context);

当然首先要写一个服务扩展

/// <summary>
    /// Ftp服务注入
    /// </summary>
    public static class FtpClientModuleprivate
    {
        /// <summary>
        /// 此处的服务为链接Ftp
        /// </summary>
        /// <param name="services"></param>
        public static void AddFtpClient(this IServiceCollection services, ServiceConfigurationContext context)
        {
            //先注入服务,不用具体管实现,全部写成一个服务去处理
            context.Services.AddSingleton<IUpLoadFtpFileService, UploadToFtpService>();
        }
    }

 

posted on 2022-05-06 13:30  白码一号  阅读(2299)  评论(4编辑  收藏  举报