c# - sftpclient - ¿Cómo puedo usar FTP para mover archivos entre directorios?
ssh net examples c# (9)
¿Qué pasa si solo tienes caminos absolutos?
Ok, me encontré con este post porque estaba recibiendo el mismo error. La respuesta parece ser el uso de una ruta relativa, que no es muy buena como solución a mi problema, ya que obtengo las rutas de las carpetas como cadenas de ruta absolutas.
Las soluciones que he encontrado sobre la marcha funcionan, pero son feas por decir lo menos. Haré de esta una respuesta de wiki de la comunidad, y si alguien tiene una solución mejor, siéntase libre de editar esto.
Desde que aprendí esto tengo 2 soluciones.
Tome la ruta absoluta de la ruta de traslado a la ruta y conviértala en una URL relativa.
public static string GetRelativePath(string ftpBasePath, string ftpToPath) { if (!ftpBasePath.StartsWith("/")) { throw new Exception("Base path is not absolute"); } else { ftpBasePath = ftpBasePath.Substring(1); } if (ftpBasePath.EndsWith("/")) { ftpBasePath = ftpBasePath.Substring(0, ftpBasePath.Length - 1); } if (!ftpToPath.StartsWith("/")) { throw new Exception("Base path is not absolute"); } else { ftpToPath = ftpToPath.Substring(1); } if (ftpToPath.EndsWith("/")) { ftpToPath = ftpToPath.Substring(0, ftpToPath.Length - 1); } string[] arrBasePath = ftpBasePath.Split("/".ToCharArray()); string[] arrToPath = ftpToPath.Split("/".ToCharArray()); int basePathCount = arrBasePath.Count(); int levelChanged = basePathCount; for (int iIndex = 0; iIndex < basePathCount; iIndex++) { if (arrToPath.Count() > iIndex) { if (arrBasePath[iIndex] != arrToPath[iIndex]) { levelChanged = iIndex; break; } } } int HowManyBack = basePathCount - levelChanged; StringBuilder sb = new StringBuilder(); for (int i = 0; i < HowManyBack; i++) { sb.Append("../"); } for (int i = levelChanged; i < arrToPath.Count(); i++) { sb.Append(arrToPath[i]); sb.Append("/"); } return sb.ToString(); } public static string MoveFile(string ftpuri, string username, string password, string ftpfrompath, string ftptopath, string filename) { string retval = string.Empty; FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(ftpuri + ftpfrompath + filename); ftp.Method = WebRequestMethods.Ftp.Rename; ftp.Credentials = new NetworkCredential(username, password); ftp.UsePassive = true; ftp.RenameTo = GetRelativePath(ftpfrompath, ftptopath) + filename; Stream requestStream = ftp.GetRequestStream(); FtpWebResponse ftpresponse = (FtpWebResponse)ftp.GetResponse(); Stream responseStream = ftpresponse.GetResponseStream(); StreamReader reader = new StreamReader(responseStream); return reader.ReadToEnd(); }
o...
Descargue el archivo desde la ruta "ftp from", cárguelo en la ruta "ftp to" y elimínelo de la ruta "ftp from".
public static string SendFile(string ftpuri, string username, string password, string ftppath, string filename, byte[] datatosend) { if (ftppath.Substring(ftppath.Length - 1) != "/") { ftppath += "/"; } FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(ftpuri + ftppath + filename); ftp.Method = WebRequestMethods.Ftp.UploadFile; ftp.Credentials = new NetworkCredential(username, password); ftp.UsePassive = true; ftp.ContentLength = datatosend.Length; Stream requestStream = ftp.GetRequestStream(); requestStream.Write(datatosend, 0, datatosend.Length); requestStream.Close(); FtpWebResponse ftpresponse = (FtpWebResponse)ftp.GetResponse(); return ftpresponse.StatusDescription; } public static string DeleteFile(string ftpuri, string username, string password, string ftppath, string filename) { FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(ftpuri + ftppath + filename); ftp.Method = WebRequestMethods.Ftp.DeleteFile; ftp.Credentials = new NetworkCredential(username, password); ftp.UsePassive = true; FtpWebResponse response = (FtpWebResponse)ftp.GetResponse(); Stream responseStream = response.GetResponseStream(); StreamReader reader = new StreamReader(responseStream); return reader.ReadToEnd(); } public static string MoveFile(string ftpuri, string username, string password, string ftpfrompath, string ftptopath, string filename) { string retval = string.Empty; byte[] filecontents = GetFile(ftpuri, username, password, ftpfrompath, filename); retval += SendFile(ftpuri, username, password, ftptopath, filename, filecontents); retval += DeleteFile(ftpuri, username, password, ftpfrompath, filename); return retval; }
Tengo un programa que necesita mover un archivo de un directorio a otro en un servidor FTP. Por ejemplo, el archivo está en:
ftp://1.1.1.1/MAIN/Dir1
y necesito mover el archivo a:
ftp://1.1.1.1/MAIN/Dir2
Encontré un par de artículos que recomiendan el uso del comando Cambiar nombre, por lo que probé lo siguiente:
Uri serverFile = new Uri(“ftp://1.1.1.1/MAIN/Dir1/MyFile.txt");
FtpWebRequest reqFTP= (FtpWebRequest)FtpWebRequest.Create(serverFile);
reqFTP.Method = WebRequestMethods.Ftp.Rename;
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(ftpUser, ftpPass);
reqFTP.RenameTo = “ftp://1.1.1.1/MAIN/Dir2/MyFile.txt";
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Pero esto no parece funcionar. Recibo el siguiente error:
El servidor remoto devolvió un error: (550) Archivo no disponible (por ejemplo, archivo no encontrado, sin acceso).
Al principio pensé que esto podría estar relacionado con los permisos, pero, por lo que puedo ver, tengo permisos para todo el sitio FTP (está en mi PC local y el uri está resuelto a localhost).
¿Debería ser posible mover archivos entre directorios como este y, de no ser así, cómo es posible?
Para abordar algunos de los puntos / sugerencias que se han planteado:
- Puedo descargar el mismo archivo desde el directorio de origen, por lo que definitivamente existe (lo que estoy haciendo es descargar primero el archivo y luego moverlo a otro lugar).
- Puedo acceder al sitio ftp desde un navegador (tanto el directorio de origen como el de destino)
- El servidor ftp se ejecuta bajo mi propia instancia de IIS en mi máquina local.
- La ruta y el caso son correctos y no hay caracteres especiales.
Además, he intentado establecer la ruta del directorio para que sea:
ftp://1.1.1.1/%2fMAIN/Dir1/MyFile.txt
Tanto para la ruta de origen como para la de destino, pero esto tampoco hace ninguna diferencia.
Encontré this artículo, que parece decir que sería útil especificar el destino como una ruta relativa; no parece ser posible especificar una ruta absoluta como destino.
reqFTP.RenameTo = “../Dir2/MyFile.txt";
¿Tienes esas carpetas definidas en el servicio FTP? ¿Se está ejecutando el servicio FTP? Si la respuesta a cualquiera de las preguntas es no, no puede usar FTP para mover los archivos.
Intente abrir la carpeta FTP en un cliente FTP y vea qué sucede. Si aún tiene un error, su definición tiene algún problema, ya que el servicio de FTP no ve la carpeta o la carpeta no está lógicamente donde cree que está en el servicio de FTP.
También puede abrir el administrador de IIS y ver cómo se configuran las cosas en FTP.
En cuanto a otros métodos para mover archivos, puede moverse fácilmente de una ruta UNC a otra, siempre que la cuenta en la que se ejecute el programa tenga los permisos adecuados para ambos. La ruta UNC es similar a una ruta HTTP o FTP, con whacks en la dirección opuesta y sin protocolo especificado.
El código parece correcto. Entonces, o bien tiene la ruta incorrecta, el archivo NO existe o necesita respetar el caso (obviamente, Windows no distingue entre mayúsculas y minúsculas, pero Linux, Unix).
¿Intentaste abrir el archivo en un navegador? Abra el Explorador de archivos de Windows, escriba la dirección en la barra de ruta y vea lo que obtiene.
En el siguiente ejemplo de código intenté con los siguientes datos y funciona.
La ubicación de inicio de sesión de FTP es "C: / FTP".
Ubicación original del archivo "C: / FTP / Data / FTP.txt".
Archivo a mover, "C: / FTP / Move / FTP.txt".
Uri serverFile = new Uri(“ftp://localhost/Data/FTP.txt");
FtpWebRequest reqFTP= (FtpWebRequest)FtpWebRequest.Create(serverFile);
reqFTP.Method = WebRequestMethods.Ftp.Rename;
reqFTP.Credentials = new NetworkCredential(ftpUser, ftpPass);
reqFTP.RenameTo = “../Move/FTP.txt";
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Estoy trabajando en el mismo tipo de proyecto, intente crear un servicio web que pueda mover los archivos y ejecutarlos en el servidor donde se encuentran sus archivos. Constrúyelo con un parámetro para pasar el nombre del archivo. Cuando comience a escribir el archivo, es posible que desee agregar un valor al nombre del archivo, diga el PK de los datos que se correlacionan con el archivo, etc.
Pude hacer que esto funcionara pero solo usando la ruta tal como existe en el servidor, es decir, /DRIVELETTER:/FOLDERNAME/filename
en RenameTo = "
Tuvo el mismo problema y encontré otra manera de resolverlo:
public string FtpRename( string source, string destination ) {
if ( source == destination )
return;
Uri uriSource = new Uri( this.Hostname + "/" + source ), UriKind.Absolute );
Uri uriDestination = new Uri( this.Hostname + "/" + destination ), UriKind.Absolute );
// Do the files exist?
if ( !FtpFileExists( uriSource.AbsolutePath ) ) {
throw ( new FileNotFoundException( string.Format( "Source ''{0}'' not found!", uriSource.AbsolutePath ) ) );
}
if ( FtpFileExists( uriDestination.AbsolutePath ) ) {
throw ( new ApplicationException( string.Format( "Target ''{0}'' already exists!", uriDestination.AbsolutePath ) ) );
}
Uri targetUriRelative = uriSource.MakeRelativeUri( uriDestination );
//perform rename
FtpWebRequest ftp = GetRequest( uriSource.AbsoluteUri );
ftp.Method = WebRequestMethods.Ftp.Rename;
ftp.RenameTo = Uri.UnescapeDataString( targetUriRelative.OriginalString );
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
return response.StatusDescription;
}
U puede usar este código:
Ubicación original del archivo " ftp://example.com/directory1/Somefile.file ".
Archivo a mover, "/newDirectory/Somefile.file".
Tenga en cuenta que la URL de destino no necesita comenzar con: ftp://example.com
NetworkCredential User = new NetworkCredential("UserName", "password");
FtpWebRequest Wr =FtpWebRequest)FtpWebRequest.Create("ftp://Somwhere.com/somedirectory/Somefile.file");
Wr.UseBinary = true;
Wr.Method = WebRequestMethods.Ftp.Rename;
Wr.Credentials = User;
Wr.RenameTo = "/someotherDirectory/Somefile.file";
back = (FtpWebResponse)Wr.GetResponse();
bool Success = back.StatusCode == FtpStatusCode.CommandOK || back.StatusCode == FtpStatusCode.FileActionOK;
MSDN parece sugerir que su ruta se considera relativa y, por lo tanto, intenta iniciar sesión en el servidor FTP utilizando las credenciales proporcionadas, luego establece el directorio actual en el directorio <UserLoginDirectory>/path
. Si este no es el mismo directorio donde está su archivo, obtendrá un error 550.