Análisis de la línea FtpWebRequest ListDirectoryDetails

Necesito ayuda para analizar la respuesta de ListDirectoryDetails en C #.

Solo necesito los siguientes campos.

  • Nombre de archivo / nombre de directorio
  • fecha de creacion
  • y el tamaño del archivo.

Esto es lo que parecen algunas líneas cuando ejecuto ListDirectoryDetails :

 d--x--x--x 2 ftp ftp 4096 Mar 07 2002 bin -rw-r--r-- 1 ftp ftp 659450 Jun 15 05:07 TEST.TXT -rw-r--r-- 1 ftp ftp 101786380 Sep 08 2008 TEST03-05.TXT drwxrwxr-x 2 ftp ftp 4096 May 06 12:24 dropoff 

Gracias por adelantado.

No estoy seguro de si todavía necesita esto, pero esta es la solución que se me ocurrió:

 Regex regex = new Regex ( @"^([d-])([rwxt-]{3}){3}\s+\d{1,}\s+.*?(\d{1,})\s+(\w+\s+\d{1,2}\s+(?:\d{4})?)(\d{1,2}:\d{2})?\s+(.+?)\s?$", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace ); 

Grupos de partidos:

  1. tipo de objeto:
    • d: directorio
    • – : archivo
  2. Matriz [3] de permisos (rwx-)
  3. Tamaño del archivo
  4. Última fecha de modificación
  5. Última hora modificada
  6. Nombre de archivo / directorio

Para esta lista específica, el siguiente código hará:

 FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://ftp.example.com/"); request.Credentials = new NetworkCredential("user", "password"); request.Method = WebRequestMethods.Ftp.ListDirectoryDetails; StreamReader reader = new StreamReader(request.GetResponse().GetResponseStream()); string pattern = @"^([\w-]+)\s+(\d+)\s+(\w+)\s+(\w+)\s+(\d+)\s+" + @"(\w+\s+\d+\s+\d+|\w+\s+\d+\s+\d+:\d+)\s+(.+)$"; Regex regex = new Regex(pattern); IFormatProvider culture = CultureInfo.GetCultureInfo("en-us"); string[] hourMinFormats = new[] { "MMM dd HH:mm", "MMM dd H:mm", "MMM d HH:mm", "MMM d H:mm" }; string[] yearFormats = new[] { "MMM dd yyyy", "MMM d yyyy" }; while (!reader.EndOfStream) { string line = reader.ReadLine(); Match match = regex.Match(line); string permissions = match.Groups[1].Value; int inode = int.Parse(match.Groups[2].Value, culture); string owner = match.Groups[3].Value; string group = match.Groups[4].Value; long size = long.Parse(match.Groups[5].Value, culture); DateTime modified; string s = Regex.Replace(match.Groups[6].Value, @"\s+", " "); if (s.IndexOf(':') >= 0) { modified = DateTime.ParseExact(s, hourMinFormats, culture, DateTimeStyles.None); } else { modified = DateTime.ParseExact(s, yearFormats, culture, DateTimeStyles.None); } string name = match.Groups[7].Value; Console.WriteLine( "{0,-16} permissions = {1} size = {2, 9} modified = {3}", name, permissions, size, modified.ToString("yyyy-MM-dd HH:mm")); } 

Obtendrás (a partir del año 2016):

 bin permissions = d--x--x--x size = 4096 modified = 2002-03-07 00:00 TEST.TXT permissions = -rw-r--r-- size = 659450 modified = 2016-06-15 05:07 TEST03-05.TXT permissions = -rw-r--r-- size = 101786380 modified = 2008-09-08 00:00 dropoff permissions = drwxrwxr-x size = 4096 modified = 2016-05-06 12:24 

Pero, en realidad, intentar analizar la lista devuelta por ListDirectoryDetails no es el camino correcto.

Desea utilizar un cliente FTP que admita el moderno comando MLSD que devuelve una lista de directorios en un formato legible por máquina especificado en el RFC 3659 . El análisis del formato legible por humanos devuelto por el antiguo comando LIST (utilizado internamente por FtpWebRequest para su método ListDirectoryDetails ) debe utilizarse como la opción de último recurso, cuando se habla con servidores FTP obsoletos, que no admiten el comando MLSD (como Microsoft Servidor FTP de IIS).

Muchos servidores usan un formato diferente para la respuesta del comando LIST . Particularmente, IIS puede usar el formato DOS. Consulte la clase C # para analizar la respuesta FTP de WebRequestMethods.Ftp.ListDirectoryDetails .


Por ejemplo, con el ensamblado WinSCP .NET , puede usar sus métodos Session.ListDirectory o Session.EnumerateRemoteFiles .

Usan internamente el comando MLSD , pero pueden recurrir al comando LIST y admiten docenas de diferentes formatos de listas legibles por humanos.

La lista devuelta se presenta como una colección de instancias de RemoteFileInfo con propiedades como:

  • Name
  • LastWriteTime (con la zona horaria correcta)
  • Length
  • FilePermissions (analizado en derechos individuales)
  • Group
  • Owner
  • IsDirectory
  • IsParentDirectory
  • IsThisDirectory

(Soy el autor de WinSCP)


La mayoría de las otras bibliotecas de terceros harán lo mismo. El uso de la clase FtpWebRequest no es confiable para este propósito. Desafortunadamente, no hay otro cliente FTP incorporado en .NET Framework.

Este es mi algoritmo para obtener el nombre de Archivo / Dir, Fecha de creación, Atributo (Archivo / Dir), Tamaño. Espero que esto ayude…

  FtpWebRequest _fwr = FtpWebRequest.Create(uri) as FtpWebRequest _fwr.Credentials = cred; _fwr.UseBinary = true; _fwr.UsePassive = true; _fwr.KeepAlive = true; _fwr.Method = WebRequestMethods.Ftp.ListDirectoryDetails; StreamReader _sr = new StreamReader(_fwr.GetResponse().GetResponseStream()); List _dirlist = new List(); List _attlist = new List(); List _datelist = new List(); List _szlist = new List(); while (!_sr.EndOfStream) { string[] buf = _sr.ReadLine().Split(' '); //string Att, Dir; int numcnt = 0, offset = 4; ; long sz = 0; for (int i = 0; i < buf.Length; i++) { //Count the number value markers, first before the ftp markers and second //the file size. if (long.TryParse(buf[i], out sz)) numcnt++; if (numcnt == 2) { //Get the attribute string cbuf = "", dbuf = "", abuf = ""; if (buf[0][0] == 'd') abuf = "Dir"; else abuf = "File"; //Get the Date if (!buf[i+3].Contains(':')) offset++; for (int j = i + 1; j < i + offset; j++) { dbuf += buf[j]; if (j < buf.Length - 1) dbuf += " "; } //Get the File/Dir name for (int j = i + offset; j < buf.Length; j++) { cbuf += buf[j]; if (j < buf.Length - 1) cbuf += " "; } //Store to a list. _dirlist.Add(cbuf); _attlist.Add(abuf); _datelist.Add(dbuf); _szlist.Add(sz); offset = 0; break; } } } 

Sobre la base de la idea original de Ryan Conrad , este es mi último código de lectura:

 protected static Regex m_FtpListingRegex = new Regex(@"^([d-])((?:[rwxt-]{3}){3})\s+(\d{1,})\s+(\w+)?\s+(\w+)?\s+(\d{1,})\s+(\w+)\s+(\d{1,2})\s+(\d{4})?(\d{1,2}:\d{2})?\s+(.+?)\s?$", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); protected static readonly String Timeformat = "MMM dd yyyy HH:mm"; ///  /// Handles file info given in the form of a string in standard unix ls output format. ///  /// The file listing string. /// A list of FtpFileInfo objects public static List GetFilesListFromFtpListingUnix(String filesListing) { List files = new List(); MatchCollection matches = m_FtpListingRegex.Matches(filesListing); if (matches.Count == 0 && filesListing.Trim('\r','\n','\t',' ').Length != 0) return null; // parse error. Could throw some kind of exception here too. foreach (Match match in matches) { FtpFileInfo fileInfo = new FtpFileInfo(); Char dirchar = match.Groups[1].Value.ToLowerInvariant()[0]; fileInfo.IsDirectory = dirchar == 'd'; fileInfo.Permissions = match.Groups[2].Value.ToCharArray(); // No clue what "inodes" actually means... Int32 inodes; fileInfo.NrOfInodes = Int32.TryParse(match.Groups[3].Value, out inodes) ? inodes : 1; fileInfo.User = match.Groups[4].Success ? match.Groups[4].Value : null; fileInfo.Group = match.Groups[5].Success ? match.Groups[5].Value : null; Int64 fileSize; Int64.TryParse(match.Groups[6].Value, out fileSize); fileInfo.FileSize = fileSize; String month = match.Groups[7].Value; String day = match.Groups[8].Value.PadLeft(2, '0'); String year = match.Groups[9].Success ? match.Groups[9].Value : DateTime.Now.Year.ToString(CultureInfo.InvariantCulture); String time = match.Groups[10].Success ? match.Groups[10].Value.PadLeft(5, '0') : "00:00"; String timeString = month + " " + day + " " + year + " " + time; DateTime lastModifiedDate; if (!DateTime.TryParseExact(timeString, Timeformat, CultureInfo.InvariantCulture, DateTimeStyles.None, out lastModifiedDate)) lastModifiedDate = DateTime.MinValue; fileInfo.LastModifiedDate = lastModifiedDate; fileInfo.FileName = match.Groups[11].Value; files.Add(fileInfo); } return files; } 

Y la clase FtpFileInfo que está llena:

 public class FtpFileInfo { public Boolean IsDirectory { get; set; } public Char[] Permissions { get; set; } public Int32 NrOfInodes { get; set; } public String User { get; set; } public String Group { get; set; } public Int64 FileSize { get; set; } public DateTime LastModifiedDate { get; set; } public String FileName { get; set; } }