目录

概述

Utilities 公用部分

GetFtpPath(path)

GetFtpPath(path, segments)

GetFtpDirectoryName(path)

GetFtpFileName(path)

GetFtpDate(date, styles)

FileSizeToString(bytes)

Stream Handling 流水处理

Exception Handling during Dispose()   Dispose()过程中的异常处理

File Listings 文件列表

Client Certificates 客户证书

Slow SSL Negotiation 缓慢的SSL协商

Handling Ungraceful Interruptions in the Control Connection 处理控制连接中的非友好中断

XCRC / XMD5 / XSHA

Pipelining

Pipelining your own Commands 对自己的命令进行管道化处理

Bulk Downloads 批量下载

结束


概述

FluentFTP是一个完全管理的FTP客户端,它被设计为易于使用和易于扩展。它支持文件和目录列表,上传和下载文件以及SSL/TLS连接。它可以连接到基于Unix和Windows/IIS的FTP服务器。这个项目完全是用托管的C#开发的。所有的功劳都归于J.P. Trosclair,他开发并维护这个库直到2016年。FluentFTP是在MIT许可下发布的,所以它既可以用于专有的也可以用于自由/开源的应用程序。

功能特点
• 完全支持FTP、FTPS(FTP over SSL)和带客户证书的FTPS
• 所有主要服务器类型(UNIX、IIS、DOS等)的文件和目录列表
• 轻松地从服务器上传和下载一个文件
• 使用标准流轻松地从服务器上读取和写入文件数据
• 创建、追加、读取、写入、重命名和删除文件和文件夹
• 递归地删除文件夹及其所有内容
• 获取文件/文件夹信息(存在、大小、安全标志、修改日期/时间)。
• 获取和设置文件权限(所有者、组、其他)。
• 绝对或相对路径(相对于 "工作目录")。
• 获取文件的哈希值/校验值(SHA-1、SHA-256、SHA-512和MD5)。
• 支持DrFTPD的PRET命令
• 支持FTP代理(User@Host, HTTP 1.1)。
• 支持SITE CHMOD命令(仅限Unix)。
• 解除符号链接的引用
• 被动和主动的数据连接(PASV, EPSV, PORT 和 EPRT)
• 所有操作的同步和异步方法(IAsyncResult模式)。
• 使用.NET的SSLStream为控制和数据连接支持显式和隐式SSL连接
• 通过为文件传输克隆FTP控制连接来提高线程安全(可选)。
• 实施自己的内部锁定,以努力保持事务的同步性
• 当服务器支持时,包括对非标准的散列/校验命令的支持
• 使用Execute()方法轻松地发布任何不支持的FTP命令,但需要数据连接的命令除外(文件列表和传输)。
• 轻松添加对更多代理类型的支持(只需扩展FTPClientProxy)。
• 轻松地添加不支持的目录列表解析器(见CustomParser例子)。
• 使用TraceListeners进行事务记录(自动省略密码)。
• 几乎所有方法的例子(见例子)
• 不支持SFTP,因为它是通过SSH的FTP,一个完全不同的协议(使用SSH.NET)。

这些文档是我拿翻译软件翻译的,可能有些地方翻译的不准确

=========================================


Utilities 公用部分

Please import FluentFTP to use these extension methods, or access them directly under the FtpExtensions class.
请导入FluentFTP以使用这些扩展方法,或者在FtpExtensions类下直接访问它们。

GetFtpPath(path)

Converts the specified local file/directory path into a valid FTP file system path
将指定的本地文件/目录路径转换为有效的FTP文件系统路径

GetFtpPath(path, segments)

Creates a valid FTP path by appending the specified segments to this string
通过将指定的片段追加到这个字符串上,创建一个有效的FTP路径。

GetFtpDirectoryName(path)

Gets the directory name of a path formatted for a FTP server
获取为FTP服务器格式化的路径的目录名称

GetFtpFileName(path)

Gets the file name and extension (if any) from the path
从路径中获取文件名和扩展名(如果有的话)。

GetFtpDate(date, styles)

Tries to convert the string FTP date representation into a date time object
试图将字符串FTP日期表示法转换为日期时间对象

FileSizeToString(bytes)

Converts a file size in bytes to a string representation (eg. 12345 becomes 12.3 KB)
将以字节为单位的文件大小转换为字符串表示(例如,12345变成12.3 KB)。


 

Stream Handling 流水处理

FluentFTP returns a Stream object for file transfers. This stream must be properly closed when you are done. Do not leave it for the GC to cleanup otherwise you can end up with uncatchable exceptions, i.e., a program crash. The stream objects are actually wrappers around NetworkStream and SslStream which perform cleanup routines on the control connection when the stream is closed. These cleanup routines can trigger exceptions so it's vital that you properly dispose the objects when you are done, no matter what. A proper implementation should go along the lines of:

FluentFTP为文件传输返回一个流对象。当你完成传输后,必须正确地关闭这个流。不要把它留给GC来清理,否则你可能会出现无法捕捉的异常,即程序崩溃。流对象实际上是NetworkStream和SslStream的封装器,当流被关闭时,它们在控制连接上执行清理程序。这些清理程序可能会触发异常,所以无论如何,当你完成后正确地处置这些对象是至关重要的。一个正确的实现应该是这样的:

try {
   using(Stream s = ftpClient.OpenRead()) {
       // perform your transfer
   }
}
catch(Exception) {
   // Typical exceptions here are IOException, SocketException, or a FtpCommandException
}

The using statement above will ensure that Dispose() is called on the stream which in turn will call Close() so that the necessary cleanup routines on the control connection can be performed. If an exception is triggered you will have a chance to catch and handle it. Another valid approach might look like so:

上面的using语句将确保在流上调用Dispose(),而Dispose()又将调用Close(),这样就可以在控制连接上执行必要的清理程序。如果触发了一个异常,你将有机会捕捉并处理它。另一种有效的方法可能是这样的:

Stream s = null;

try {
    s = ftpClient.OpenRead();
    // perform transfer
}
finally {
     if(s != null)
           s.Close()
}

The finally block above ensures that Close() is always called on the stream even if a problem occurs. When Close() is called any resulting exceptions can be caught and handled accordingly.
上面的finally块确保Close()总是在流上被调用,即使有问题发生。当Close()被调用时,任何产生的异常都可以被捕获并进行相应处理。上面的finally块确保Close()总是在流上被调用,即使有问题发生。当Close()被调用时,任何产生的异常都可以被捕获并进行相应处理。


 

Exception Handling during Dispose()   Dispose()过程中的异常处理

FluentFTP includes exception handling in key places where uncatchable exceptions could occur, such as the Dispose() methods. The problem is that part of the cleanup process involves closing out the internal sockets and streams. If Dispose() was called because of an exception and triggers another exception while trying to clean-up you could end up with an un-catchable exception resulting in an application crash. To deal with this FtpClient.Dispose() and FtpSocketStream.Dispose() are setup to handle SocketException and IOException and discard them. The exceptions are written to the FtpTrace TraceListeners for debugging purposes, in an effort to not hide important errors while debugging problems with the code.

The exception that propagates back to your code should be the root of the problem and any exception caught while disposing would be a side affect however while testing your project pay close attention to what's being logged via FtpTrace. See the Debugging example for more information about using TraceListener objects with FluentFTP.

FluentFTP在可能发生不可捕捉的异常的关键地方包括了异常处理,例如Dispose()方法。问题是,清理过程的一部分涉及到关闭内部的套接字和流。如果Dispose()被调用是因为出现了异常,并且在试图清理的时候触发了另一个异常,那么最终可能会出现无法捕捉的异常,导致应用程序崩溃。为了处理这个问题,FtpClient.Dispose()和FtpSocketStream.Dispose()被设置为处理SocketException和IOException并丢弃它们。异常被写到FtpTrace TraceListeners中用于调试,目的是为了在调试代码问题时不隐藏重要错误。

传播到你的代码中的异常应该是问题的根源,在处理时捕获的任何异常都是副作用,但是在测试你的项目时要密切注意通过FtpTrace记录的内容。关于在FluentFTP中使用TraceListener对象的更多信息,请参阅调试实例。

File Listings 文件列表

Some of you may already be aware that RFC959 does not specify any particular format for file listings (LIST). As time has passed extensions have been added to address this problem. Here's what you need to know about the situation:

1.UNIX File Listings : UNIX style file listings are NOT reliable. Most FTP servers respond to LIST with a format that strongly resembles the output of ls -l on UNIX and UNIX-like operating systems. This format is difficult to parse and has shortcomings with date/time values in which assumptions have to made in order to try to guess an as accurate as possible value. FluentFTP provides a LIST parser but there is no guarantee that it will work right 100% of the time. You can add your own parser if it doesn't. See the examples project in the source code. You should include the FtpListOption.Modify flag for the most accurate modification dates (down to the second). MDTM will fail on directories on most but not all servers. An attempt is made by FluentFTP to get the modification time of directories using this command but do not be surprised if it fails. Modification times of directories should not be important most of the time. If you think they are you might want to reconsider your reasoning or upgrade the server software to one that supports MLSD (machine listings: the best option).

2.DOS/IIS File Listings : DOS style file listings (default IIS LIST response) are mostly supported. There is one issue where a file or directory that begins with a space won't be correctly parsed because of the arbitrary amount of spacing IIS throws in its directory listings. IIS can be configured to throw out UNIX style listings so if this is an issue for you, you might consider enabling that option. If you know a better way to parse the listing you can roll your own parser per the examples included with the downloads. If it works share it! It's also worth noting that date/times in DOS style listings don't include seconds. As mentioned above, if you pass the FtpListOption.Modify flag to GetListing() MDTM will be used to get the accurate (to the second) date/time however MDTM on directories does not work with IIS.

3.Machine Listings : FluentFTP prefers machine listings (MLST/MLSD) which are an extension added to the protocol. This format is reliable and is always used over LIST when the server advertises it in its FEATure list unless you override the behavior with FtpListOption flags. With machine listings you can expect a correct file size and modification date (UTC). If you run across a case that you are not it's very possible it's due to a bug in the machine listing parser and you should report the issue along with a sample of the file listing (see the debugging example in the source).

4.Name Listings : Name Listings (NLST) are the next best thing when machine listings are not available however they are MUCH slower than either LIST or MLSD. This is because NLST sends a list of objects in the directory and the server has to be queried for the rest of the information on file-by-file basis, such as the file size, the modification time and an attempt to determine if the object is a file or directory. Name listings can be forced using FtpListOption flags. The best way to handle falling back to NLST is to query the server features (FtpClient.Capabilities) for the FtpCapability.MLSD flag. If it's not there, then pass the necessary flags to GetListing() to force a name listing.

你们中的一些人可能已经知道,RFC959并没有为文件列表(LIST)指定任何特定的格式。随着时间的推移,已经添加了一些扩展来解决这个问题。下面是你需要了解的情况:

1.UNIX文件列表:UNIX风格的文件列表是不可靠的。大多数FTP服务器对LIST的响应格式与UNIX和类UNIX操作系统上ls -l的输出非常相似。这种格式很难解析,而且在处理日期/时间值时也有缺陷,必须做出假设才能尽量准确地猜出一个值。FluentFTP提供了一个LIST解析器,但不能保证它在100%的情况下都能正确工作。如果它不工作,你可以添加你自己的解析器。请看源代码中的例子项目。你应该包括FtpListOption.Modify标志以获得最准确的修改日期(精确到秒)。MDTM在大多数但不是所有的服务器上的目录都会失败。FluentFTP会尝试用这个命令来获取目录的修改时间,但如果失败了也不要惊讶。目录的修改时间在大多数情况下并不重要。如果你认为它们很重要,你可能要重新考虑你的理由,或者将服务器软件升级到支持MLSD的软件(机器列表:最好的选择)。

2.DOS/IIS文件列表: DOS风格的文件列表(默认的IIS LIST响应)大多被支持。有一个问题,即以空格开头的文件或目录不会被正确解析,因为IIS在其目录列表中抛出了任意数量的间距。IIS可以被配置为抛出UNIX风格的列表,所以如果这对你来说是个问题,你可以考虑启用这个选项。如果你知道一个更好的方法来解析列表,你可以根据下载的例子推出自己的解析器。如果它有效,请分享它!同样值得注意的是,DOS风格的列表中的日期/时间不包括秒。如上所述,如果你向GetListing()传递FtpListOption.Modify标志,MDTM将被用来获取准确的(精确到秒)日期/时间,但是目录上的MDTM在IIS中不起作用。

3.机器列表: FluentFTP更倾向于使用机器列表(MLST/MLSD),这是一种添加到协议中的扩展。这种格式是可靠的,当服务器在其FEATure列表中公布它时,总是使用LIST,除非你用FtpListOption标志来覆盖这种行为。对于机器列表,你可以期待一个正确的文件大小和修改日期(UTC)。如果你遇到不正确的情况,很可能是由于机器列表解析器的错误造成的,你应该把这个问题和文件列表的样本一起报告(见源代码中的调试例子)。

4.名称列表:当机器列表不可用时,名称列表(NLST)是次要的东西,但是它们比LIST或MLSD慢得多。这是因为NLST发送的是目录中的对象列表,而服务器必须逐个文件地查询其余的信息,比如文件大小、修改时间和试图确定对象是文件还是目录。可以使用FtpListOption标志强制列出名称。处理返回NLST的最好方法是查询服务器功能(FtpClient.Capabilities)中的FtpCapability.MLSD标志。如果没有,就把必要的标志传给GetListing(),强制列出一个名字。


 

Client Certificates 客户证书

When you are using Client Certificates, be sure that:
You use X509Certificate2 objects, not the incomplete X509Certificate implementation.
You do not use pem certificates, use p12 instead. See this Stack Overflow thread for more information. If you get SPPI exceptions with an inner exception about an unexpected or badly formatted message, you are probably using the wrong type of certificate.

当你使用客户证书时,要确保:
你使用X509Certificate2对象,而不是不完整的X509Certificate实现。
你不使用pem证书,而是使用p12。更多信息请参见 Stack Overflow 线程。如果你得到SPPI异常,其中有一个关于意外或格式不好的消息的内部异常,你可能使用了错误的证书类型。

Slow SSL Negotiation 缓慢的SSL协商

FluentFTP uses SslStream under the hood which is part of the .NET framework. SslStream uses a feature of windows for updating root CA's on the fly, at least that's the way I understand it. These updates can cause a long delay in the certificate authentication process which can cause issues in FluentFTP related to the SocketPollInterval property used for checking for ungraceful disconnections between the client and server. This MSDN Blog covers the issue with SslStream and talks about how to disable the auto-updating of the root CA's.
The latest builds of FluentFTP log the time it takes to authenticate. If you think you are suffering from this problem then have a look at Examples\Debug.cs for information on retrieving debug information.

FluentFTP使用的是.NET框架中的SslStream。SslStream使用windows的一个功能,用于实时更新根CA,至少我是这么理解的。这些更新可能会在证书认证过程中造成长时间的延迟,这可能会导致FluentFTP中与SocketPollInterval属性有关的问题,该属性用于检查客户端和服务器之间是否有不礼貌的断开。这个MSDN博客涵盖了SslStream的问题,并谈到了如何禁用根CA的自动更新功能。
FluentFTP的最新版本记录了认证的时间。如果你认为你正在遭受这个问题,那么请看 Examples\Debug.cs 中关于检索调试信息的信息。


 

Handling Ungraceful Interruptions in the Control Connection 处理控制连接中的非友好中断

FluentFTP uses Socket.Poll() to test for connectivity after a user-definable period of time has passed since the last activity on the control connection. When the remote host closes the connection there is no way to know, without triggering an exception, other than using Poll() to make an educated guess. When the connectivity test fails the connection is automatically re-established. This process helps a great deal in gracefully reconnecting however it does not eliminate your responsibility for catching IOExceptions related to an ungraceful interruption in the connection. Usually, maybe always, when this occurs the InnerException will be a SocketException. How you want to handle the situation from there is up to you.

FluentFTP使用Socket.Poll()来测试在控制连接上的最后一次活动过后的一段用户可定义的时间后的连接情况。当远程主机关闭连接时,除了使用Poll()进行有根据的猜测外,没有办法在不引发异常的情况下知道。当连接测试失败时,连接会自动重新建立。这个过程对优雅地重新连接有很大的帮助,但是它并没有消除你捕捉与连接中不优雅的中断有关的IOExceptions的责任。通常,也许总是这样,当这种情况发生时,InnerException将是一个SocketException。你想如何处理这种情况,取决于你。

try {
    // ftpClient.SomeMethod();
}
catch(IOException e) {
    if(e.InnertException is SocketException) {
         // the control connection was interrupted
    }
}


XCRC / XMD5 / XSHA

XCRC, XMD5, and XSHA are non standard commands and to the best that I can tell contain no kind of formal specification. Support for them exists as extension methods in the FluentFTP.Extensions namespace as of the latest revision. They are not guaranteed to work and you are strongly encouraged to check the FtpClient.Capabilities flags for the respective flag (XCRC, XMD5, XSHA1, XSHA256, XSHA512) before calling these methods.
Support for the MD5 command as described here has also been added. Again, check for FtpFeature.MD5 before executing the command.
Experimental support for the HASH command has been added to FluentFTP. It supports retrieving SHA-1, SHA-256, SHA-512, and MD5 hashes from servers that support this feature. The returned object, FtpHash, has a method to check the result against a given stream or local file. You can read more about HASH in this draft.

XCRC、XMD5和XSHA是非标准的命令,据我所知,它们没有正式的规范。在最新的版本中,FluentFTP.Extensions命名空间对它们的支持是作为扩展方法存在的。我们强烈建议你在调用这些方法之前检查FtpClient.Capabilities的相关标志(XCRC, XMD5, XSHA1, XSHA256, XSHA512)。
还增加了对这里描述的MD5命令的支持。同样,在执行该命令前要检查FtpFeature.MD5。
FluentFTP中加入了对HASH命令的实验性支持。它支持从支持该功能的服务器上检索SHA-1、SHA-256、SHA-512和MD5哈希值。返回的对象FtpHash有一个方法可以根据给定的流或本地文件检查结果。你可以在这个草案中阅读更多关于HASH的内容。


 

Pipelining

If you just wanting to enable pipelining (in FtpClient and FtpControlConnection), set the EnablePipelining property to true. Hopefully this is all you need but it may not be. Some servers will drop the control connection if you flood it with a lot of commands. This is where the MaxPipelineExecute property comes into play. The default value here is 20, meaning that if you have 100 commands queued, 20 of the commands will be written to the underlying socket and 20 responses will be read, then the next 20 will be executed, and so forth until the command queue is empty. The value 20 is not a magic number, it's just the number that I deemed stable in most scenarios. If you increase the value, do so knowing that it could break your control connection.

如果你只是想启用流水线(在FtpClient和FtpControlConnection中),将EnablePipelining属性设置为true。希望这就是你所需要的,但也可能不是。如果你用大量的命令淹没控制连接,有些服务器会放弃控制连接。这就是MaxPipelineExecute属性起作用的地方。这里的默认值是20,意味着如果你有100个命令排队,其中20个命令将被写入底层套接字,20个响应将被读取,然后下一个20个将被执行,以此类推直到命令队列为空。20这个值并不是一个神奇的数字,它只是我认为在大多数情况下稳定的数字。如果你增加这个值,要知道这可能会破坏你的控制连接。


 

Pipelining your own Commands 对自己的命令进行管道化处理

Pipelining your own commands is not dependent on the EnablePipelining feature. The EnablePipelining property only applies to internal pipelining performed by FtpClient and FtpControlConnection. You can use the facilities for creating pipelines at your own discretion.

If you need to cancel your pipeline in the middle of building your queue, you use the CancelPipeline() method. These methods are implemented in the FtpControlConnection class so people that are extending this class also have access to them. This feature is also used in FtpClient.GetListing() to retrieve last write times of the files in the listing when the LIST command is used.

You don't need to worry about locking the command channel (LockControlConnection() or UnlockControlConnection()) because the code that handles executing the pipeline does so for you.

Here's a quick example:

对自己的命令进行管道化并不依赖于EnablePipelining功能。EnablePipelining属性只适用于由FtpClient和FtpControlConnection执行的内部管道化。你可以自行决定使用创建流水线的设施。

如果你需要在建立队列的过程中取消管道,你可以使用 CancelPipeline() 方法。这些方法是在FtpControlConnection类中实现的,所以扩展这个类的人也可以使用这些方法。在FtpClient.GetListing()中也使用了这个功能,当使用LIST命令时,可以检索列表中的文件的最后写入时间。

你不需要担心锁定命令通道(LockControlConnection()或UnlockControlConnection()),因为处理执行管道的代码会帮你这样做。

这里有一个简单的例子:

FtpClient cl = new FtpClient();

//...

// initalize the pipeline
cl.BeginExecute();

// execute commands as normal
cl.Execute("foo");
cl.Execute("bar");
cl.Execute("baz");

//...

// execute the queued commands
FtpCommandResult[] res = cl.EndExecute();

// check the result status of the commands
foreach(FtpCommandResult r in res) {
    if(!r.ResponseStatus) {
          // we have a failure
    }
}


 

Bulk Downloads 批量下载

When doing a large number of transfers, one needs to be aware of some inherit issues with data streams. When a socket is opened and then closed, the socket is left in a linger state for a period of time defined by the operating system. The socket cannot reliably be re-used until the operating system takes it out of the TIME WAIT state. This matters because a data stream is opened when it's needed and closed as soon as that specific task is done:

Download File
Open Data Stream
Read bytes
Close Data Stream
This is not a bug in FluentFTP. RFC959 says that EOF on stream mode transfers is signaled by closing the connection. On downloads and file listings, the sockets being used on the server will stay in the TIME WAIT state because the server closes the socket when it's done sending the data. On uploads, the client sockets will go into the TIME WAIT state because the client closes the connection to signal EOF to the server.

RFC959 defines another data mode called block that allows persistent data connections but it is not implemented by this library and will not be in the foreseeable future. Support for block transfers on the server side of things is not that common. I know IIS supports them however I cannot name a single other server that implements MODE B. I cannot justify making an already complicated process more so by adding in a feature that just isn't that likely to be used.

当进行大量的传输时,需要注意数据流的一些固有问题。当一个套接字被打开然后关闭时,该套接字会在操作系统定义的一段时间内处于滞留状态。在操作系统将其从TIME WAIT状态中取出之前,该套接字不能被可靠地重新使用。这一点很重要,因为数据流在需要时被打开,一旦该特定任务完成就会被关闭:

下载文件
打开数据流
读取字节
关闭数据流
这不是FluentFTP的一个错误。RFC959说,流模式传输的EOF是通过关闭连接来表示的。在下载和文件列表中,服务器上正在使用的套接字将保持在TIME WAIT状态,因为服务器在发送完数据后将关闭套接字。在上传时,客户端的套接字将进入时间等待状态,因为客户端关闭连接以向服务器发出EOF信号。

RFC959定义了另一种叫做block的数据模式,它允许持久的数据连接,但它没有被这个库实现,在可预见的将来也不会实现。在服务器端对块传输的支持并不常见。我知道IIS支持它们,但是我不能说出其他任何一个服务器实现了MODE B。我不能证明通过添加一个不太可能被使用的功能而使一个已经很复杂的过程变得更加复杂。

结束

如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言

end

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐