Indy FTP List方法中的Delphi Bug?

时间:2015-06-16 12:23:34

标签: delphi ftp indy

我试图生成一个与某个文件掩码匹配的文件列表,并且Indy因此错误而失败

  

EidReplyRFCError,带有消息' ':没有这样的文件或目录。

我尝试了几种变体,这就是结果:

FTP.List( aFiles, '', true ); =>这工作

FTP.List( aFiles, '*.*', false ); =>这也有效

FTP.List( aFiles, '*.*', true ); =>

FTP.List( aFiles, '*.zip', true ); =>这也失败了(尽管它是最新文档中的例子)

FTP.List( '*.*', false ); =>这工作

FTP.List( '*.*', true ); =>

我正在使用Delphi XE5& Indy版本10.6。如果相关,XE8中也存在同样的问题。

也许功能已经改变,文档现在出错了,或者它是Indy中的一个错误?

我需要"详细信息"所以我可以比较时间戳&大小也是。

2 个答案:

答案 0 :(得分:11)

这不是TIdFTP中的错误。这在Indy文档中更多的是遗漏。

EIdReplyRFCError表示FTP服务器本身报告错误以响应TIdFTP.List()正在发送的命令。根据{{​​1}}参数和ADetails TIdFTP + UseMLIS属性的值,CanUseMLS可以发送三种不同命令之一:< / p>

List()

因此:

ADetails=False:

    NLST [ASpecifier]

ADetails=True:

  TIdFTP.UseMLIS=True and TIdFTP.CanUseMLS=True:

    MLSD [ASpecifier]

  TIdFTP.UseMLIS=False or TIdFTP.CanUseMLS=False:

    LIST [ASpecifier]

请注意所有&#34;失败的命令&#34;有一些共同点 - 他们可能正在发送FTP.List( aFiles, '', true ); // this works // sends either 'LIST' or 'MLSD' FTP.List( aFiles, '*.*', false ); // this works too // sends 'NLST *.*' FTP.List( aFiles, '*.*', true ); // this fails // sends either 'LIST *.*' or 'MLSD *.*' FTP.List( aFiles, '*.zip', true ); // this fails too // sends either 'LIST *.zip' or 'MLSD *.zip' FTP.List( '*.*', false ); // this works // sends 'NLST *.*' FTP.List( '*.*', true ); // this fails // sends either 'LIST *.*' or 'MLSD *.*' 命令。

RFC 959,定义MLSD ASpecifierLIST命令:

  

列表(列表)

     

此命令使列表从服务器发送到       被动DTP。 如果路径名指定目录或其他目录       一组文件,服务器应该传输一个文件列表       在指定的目录中。如果路径名指定了       文件然后服务器应该发送当前信息       文件。 null参数意味着用户当前正在工作或       默认目录。 ...

     

姓名单(NLST)

     

此命令导致从中发送目录列表       服务器到用户站点。 路径名应指定一个       目录或其他特定于系统的文件组描述符;一个       null参数表示当前目录。 ...

Per RFC 3659,定义NLST命令:

  

MLST和MLSD命令都允许一个可选参数。      此参数可以是目录名称,也可以是仅限MLST的a      文件名。出于这些目的,一个&#34;文件名&#34;是任何名称      服务器NVFS中的实体不是目录。 TVFS在哪里      支持,任何TVFS相对路径名在当前工作中有效      可以给出目录或任何TVFS完全限定的路径名​​。 如果是      给出目录名,然后MLSD必须返回一个列表      命名目录的内容,否则发出501回复,和      不会打开数据连接。 ...

     

如果没有给出参数,那么MLSD必须返回一个列表      当前工作目录的内容,而MLST必须返回一个      列出提供有关当前工作目录的信息      本身。 ...

     

...

     

如果Client-FTP发送了无效参数,则服务器-UT必须      回复,错误代码为501。

MLSD*.*不是目录名,因此如果*.zip发送TIdFTP.List()MLSD *.*命令,服务器将失败。因此,MLSD *.zipTIdFTP.UseMLIS在您的情况下可能都是True(TIdFTP.CanUseMLS默认为True,而UseMLIS在现代FTP服务器上通常为True)。

CanUseMLS命令不支持像MLSD / LIST命令那样的服务器端过滤。因此,您不能将NLST*.**.zip一起使用。您必须检索完整的目录列表,然后忽略您不感兴趣的任何条目。否则,在调用MLSD之前将TIdFTP.UseMLIS设置为False,但是您冒着{{1}的风险错误地解析某些服务器的目录列表,因为TIdFTP.List()命令使用的格式从未标准化,并且在整个Internet上使用了数百种自定义格式(以及Indy 10中为什么TIdFTP.DirectoryListing包括使用LIST时的数十个列表解析器。与TIdFTP不同,LIST具有标准化格式(这就是为什么它首先被引入,以替换MLSx的缺点)。

因此,这一切归结为 - 当LISTTIdFTP.UseMLIS都为True时,TIdFTP.CanUseMLS 必须必须为空白或目录,文件掩码。

TIdFTP.List() documentation 表示ASpecifier可以在内部调用TIdFTP.ExtListDir()发送List()命令,但没有具体提及此特定内容在这种情况下对MLSD参数的限制:

  

如果CanUseMLS包含True,则调用ExtListDir以在ADest参数变量中捕获并存储FTP MLSD命令的结果,而不是LIST或NLST命令。在这种情况下,List方法中不执行其他处理,并退出该方法。

     

当ADetails为False时,只使用FTP NLST命令在ADest字符串列表中返回文件或目录名称。当ADetails为True时,List可以使用FTP LIST命令返回FTP服务器相关的详细信息,包括文件大小,修改日期以及所有者,组和用户的文件权限。

TIdFTP.ExtListDir() documentation 表明其输入参数必须是目录名,但是:

  

ExtListDir支持的MLSD命令接受目录列表中的可选目录名称或Adirectory中的相对路径。如果在ADirectory中传递了空字符串,则当前目录用于目录列表操作。

旁注:ASpecifier属性会告诉您TIdFTP.DirFormat解析结果后检测到的列表格式。或者,您可以查看TIdFTP.DirectoryListing的{​​{1}}和Details属性(将其类型转换为UsedMLS以访问属性)以推断{{1}发送的命令(如果成功的话)。

答案 1 :(得分:0)

另一种解决方案是包括

IdAllFTPListParsers

在您的uses子句中并禁用UseMLIS

像这样:

uses
  ....
  IdAllFTPListParsers;
   .....
procedure TForm1.DoThis;
var
  i: integer;
begin
  if not IDFTP1.Connected then IDFTP1.Connect;
  IDFTP1.UseMLIS:= false;
  IDFTP1.List;
  for i:= 0 to IDFTP1.DirectoryListing.Count -1 do begin
    .. process directory items.
    IdFTP1.TransferType:= ftBinary;
    ..Get your files