C#Descargar todos los archivos y subdirectorios a través de FTP
.net ftpwebrequest (1)
FtpWebRequest
no tiene ningún soporte explícito para las operaciones recursivas de archivos (incluidas las descargas).
Tienes que implementar la recursión tú mismo:
- Listar el directorio remoto
- Itere las entradas, descargue archivos y vuelva a utilizarlos en subdirectorios (enumerándolos nuevamente, etc.)
Parte difícil es identificar archivos de subdirectorios.
No hay forma de hacerlo de forma portátil con
FtpWebRequest
.
Lamentablemente,
FtpWebRequest
no admite el comando
MLSD
, que es la única forma portátil de recuperar la lista de directorios con atributos de archivo en el protocolo FTP.
Consulte también
Verificar si el objeto en el servidor FTP es un archivo o directorio
.
Sus opciones son:
- Realice una operación en un nombre de archivo que seguramente fallará para el archivo y tenga éxito para los directorios (o viceversa). Es decir, puede intentar descargar el "nombre". Si eso tiene éxito, es un archivo, si eso falla, es un directorio.
- Puede ser afortunado y, en su caso específico, puede distinguir un archivo de un directorio por un nombre de archivo (es decir, todos sus archivos tienen una extensión, mientras que los subdirectorios no)
-
Utiliza una lista de directorio larga (comando
LIST
= métodoListDirectoryDetails
) e intenta analizar una lista específica del servidor. Muchos servidores FTP usan una lista * nix-style, donde identifica un directorio por lad
al comienzo de la entrada. Pero muchos servidores usan un formato diferente. El siguiente ejemplo utiliza este enfoque (asumiendo el formato * nix)
void DownloadFtpDirectory(string url, NetworkCredential credentials, string localPath)
{
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (FtpWebResponse listResponse = (FtpWebResponse)listRequest.GetResponse())
using (Stream listStream = listResponse.GetResponseStream())
using (StreamReader listReader = new StreamReader(listStream))
{
while (!listReader.EndOfStream)
{
lines.Add(listReader.ReadLine());
}
}
foreach (string line in lines)
{
string[] tokens =
line.Split(new[] { '' '' }, 9, StringSplitOptions.RemoveEmptyEntries);
string name = tokens[8];
string permissions = tokens[0];
string localFilePath = Path.Combine(localPath, name);
string fileUrl = url + name;
if (permissions[0] == ''d'')
{
if (!Directory.Exists(localFilePath))
{
Directory.CreateDirectory(localFilePath);
}
DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath);
}
else
{
FtpWebRequest downloadRequest = (FtpWebRequest)WebRequest.Create(fileUrl);
downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
downloadRequest.Credentials = credentials;
using (FtpWebResponse downloadResponse =
(FtpWebResponse)downloadRequest.GetResponse())
using (Stream sourceStream = downloadResponse.GetResponseStream())
using (Stream targetStream = File.Create(localFilePath))
{
byte[] buffer = new byte[10240];
int read;
while ((read = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
{
targetStream.Write(buffer, 0, read);
}
}
}
}
}
Use la función como:
NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/directory/to/download/";
DownloadFtpDirectory(url, credentials, @"C:/target/directory");
Si desea evitar problemas al analizar los formatos de listado de directorios específicos del servidor, use una biblioteca de terceros que admita el comando
MLSD
y / o analice varios formatos de listado
LIST
;
y descargas recursivas.
Por ejemplo, con el
ensamblado WinSCP .NET
puede descargar todo el directorio con una sola llamada a la
Session.GetFiles
.
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Download files
session.GetFiles("/directory/to/download/*", @"C:/target/directory/*").Check();
}
Internamente, WinSCP utiliza el comando
MLSD
, si el servidor lo admite.
De lo contrario, utiliza el comando
LIST
y admite docenas de diferentes formatos de listado.
El Session.GetFiles es recursivo por defecto.
(Soy el autor de WinSCP)
Información general
Todavía estoy en el proceso de aprender C #.
Para ayudarme, estoy tratando de crear un programa que sincronice automáticamente todos mis proyectos locales con una carpeta en mi servidor FTP.
Esto para que, ya sea que esté en la escuela o en casa, siempre tenga los mismos proyectos disponibles.
Sé que hay programas como Dropbox que ya hacen esto por mí, pero pensé que crear algo así me enseñaría mucho en el camino.
El problema
Mi primer paso hacia mi objetivo fue simplemente descargar todos los archivos, subdirectorios y subarchivos de mi servidor FTP.
Me las arreglé para descargar todos los archivos de un directorio con el siguiente código.
Sin embargo, mi código solo enumera los nombres de carpetas y los archivos en el directorio principal.
Las subcarpetas y subfiles nunca se devuelven y nunca se descargan.
Aparte de eso, el servidor devuelve un error 550 porque estoy tratando de descargar las carpetas como si fueran archivos.
He estado en esto por más de 4 horas, pero no puedo encontrar nada sobre cómo solucionar estos problemas y hacer que funcione.
Por lo tanto, espero que ustedes me ayuden :)
Código
public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
WebResponse response = null;
StreamReader reader = null;
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
response = request.GetResponse();
reader = new StreamReader(response.GetResponseStream());
string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("/n");
line = reader.ReadLine();
}
result.Remove(result.ToString().LastIndexOf(''/n''), 1);
return result.ToString().Split(''/n'');
}
catch (Exception ex)
{
if (reader != null)
{
reader.Close();
}
if (response != null)
{
response.Close();
}
downloadFiles = null;
return downloadFiles;
}
}
private void Download(string file)
{
try
{
string uri = url + "/" + file;
Uri serverUri = new Uri(uri);
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return;
}
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url + "/" + file);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
FileStream writeStream = new FileStream(localDestnDir + "//" + file, FileMode.Create);
int Length = 2048;
Byte[] buffer = new Byte[Length];
int bytesRead = responseStream.Read(buffer, 0, Length);
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = responseStream.Read(buffer, 0, Length);
}
writeStream.Close();
response.Close();
}
catch (WebException wEx)
{
MessageBox.Show(wEx.Message, "Download Error");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Download Error");
}
}