Alters

Remote Tools: Teoría de programación (IV) - FTP con VB.net

Buenas!

Lo prometido es deuda, así que vamos a ver (más bien a repasar) el tema de FTP.

Tengo varias cosas que contar antes de empezar (como de costumbre)...

Estoy preparando mucho (MUCHO) material nuevo para el blog, solo necesito tiempo para estructurarlo... será un curso entero, también (no tan extenso como el de matemáticas, pero tendrá "chicha").

¡¡Ya hemos lanzado nuestro canal de YouTube!! Aquí podéis encontrarlo. Podréis ver cómo DoH (yo), Meryhell (co-propietaria de Cosplay & Co, junto a mi), y James_Strange jugamos a nuestros juegos favoritos... a veces logran sacarnos de quicio (literalmente), jejeje :-)





Cuando acabe esta saga (es decir, antes de empezar el tema III de matemáticas), habrá una pequeña entrada de "Las aventuras de DoHITB"...

Dejando de lado la "vida privada", vayamos a lo que hemos venido a hacer... ¡FTP con VB.net!

En esta entrada tenéis una visión globlal del uso de FTP bajo VB.net.

Vamos, partiendo de esa base, a realizar algunas mejoras; a la vez que dejaremos el código más accesible y configurable.

El código original se basaba, principalmente, en dos funciones públicas:

subirFichero(ByVal fichero As String, ByVal destino As String, ByVal dir As String) As String

bajarArchivo(ByVal fichero As String, ByVal destino As String) As Boolean

A su vez, éstos delegaban en varias funciones privadas, que gestionaban temas como ver si el directorio a acceder existía, o si existía cierto archivo.

Como dijimos antes, vamos a realizar algunas mejoras en el código para dejar la clase más accesible, y sobretodo, más "configurable".

Vamos allá.

Empezamos con los métodos públicos.

subirFichero:

a) Lo renombraremos, usando una notación "estándar", por "subirObjeto".

b) Modificaremos un poco el código:

 Public Function subirObjeto(ByVal objeto As String, ByVal destino As String, ByVal dir As String) As String
        Dim infoFichero As New FileInfo(objeto)
        Dim uri As String = destino

        If Not existeObjeto(dir) and Me.crearSiNo Then
            creaDirectorio(dir)
Else
Return "No se ha subido el archivo por que no existe el directorio"
        End If

        Dim peticionFTP As FtpWebRequest = CType(FtpWebRequest.Create(New Uri(destino)), FtpWebRequest)

Me.configurarFTP(peticionFTP, WebRequestMethods.Ftp.UploadFile)
        peticionFTP.KeepAlive = False
        peticionFTP.UsePassive = False
        peticionFTP.UseBinary = Me.useBinary
        peticionFTP.ContentLength = infoFichero.Length
        Dim lector As Byte() = New Byte(Me.longitudBuffer) {}
        Dim num As Integer
        Dim fs As FileStream = infoFichero.OpenRead()

        Try
            Dim escritor As Stream = peticionFTP.GetRequestStream()
            num = fs.Read(lector, 0, Me.longitudBuffer)

            While (num <> 0)
                escritor.Write(lector, 0, num)
                num = fs.Read(lector, 0, longitudBuffer)
            End While

            escritor.Close()
            fs.Close()

            Return String.Empty
        Catch ex As Exception
            Return ex.Message
        End Try
    End Function

Vemos varios cambios en el cuerpo de la función:


  • Variable de clase "crearSiNo": podemos hacer que, en caso de no existir el directorio, retorne un error.
  • Método "configurarFTP" : configura la parte común de todos los métodos (respecto al FTPWebRequest)
  • Variable de clase "useBinary": podemos especificar si la transferencia será en modo binario (true) o no (false)
  • Variable de clase "longitudBuffer": podemos indicar el tamaño de buffer, y variarlo entre llamadas (dependiendo, por ejemplo, del tamaño de archio o la carga de memoria de la máquina)


Vamos a ver ahora "bajarArchivo" (rebautizado como "bajarObjeto")

Public Function bajarObjeto(ByVal fichero As String, ByVal destino As String) As Boolean
        Dim localfile As String = destino
        Dim peticionFTP As FtpWebRequest = CType(FtpWebRequest.Create(New Uri(fichero)), FtpWebRequest)

Me.configurarFTP(peticionFTP, WebRequestMethods.Ftp.DownloadFile)
        peticionFTP.KeepAlive = False
        peticionFTP.UsePassive = False

        Try
            Using response As System.Net.FtpWebResponse = CType(peticionFTP.GetResponse, System.Net.FtpWebResponse)
                Using responseStream As IO.Stream = response.GetResponseStream
                    Using fs As New IO.FileStream(localfile, IO.FileMode.Create)
                        Dim buffer(longitudBuffer - 1) As Byte
                        Dim read As Integer = 0

                        Do
                            read = responseStream.Read(buffer, 0, buffer.Length)
                            fs.Write(buffer, 0, read)
                        Loop Until read = 0
                        responseStream.Close()
                        fs.Flush()
                        fs.Close()
                    End Using
                    responseStream.Close()
                End Using
                response.Close()
            End Using

            Return true
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

        Return false
    End Function

Cambios realizados:

  • Cambio en el nombre del método
  • Eliminada variable de retorno; ahora el retorno se hace de manera directa (Return true / Return false)
  • Optimizado el código con la función "configurarFTP".

Veamos, ahora, el método privado "configurarFTP":

Private Sub configurarFTP(ByRef ftp As FtpWebRequest, ByVal tipo As String)
peticionFTP.Credentials = New NetworkCredential(user, pass)
peticionFTP.Method = tipo
End Sub

Como vemos, son dos líneas de nada. Pero dos líneas que nos ahorramos por método, a fin de cuentas es un ahorro (aunque pequeño), ¿no? :-)

Vamos ahora con las funciones privadas:

Empezamos por "existeObjeto":

    Private Function existeObjeto(ByVal dir As String) As Boolean
Dim peticionFTP As FtpWebRequest = CType(WebRequest.Create(New Uri(dir)), FtpWebRequest)

Me.configurarFTP(peticionFTP, WebRequestMethods.Ftp.GetDateTimestamp)
        peticionFTP.UsePassive = False

        Try
            Dim respuestaFTP As FtpWebResponse = CType(peticionFTP.GetResponse(), FtpWebResponse)
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function

Mejoras:

  • Optimizado código usando la función "configurarFTP".


Echemos un ojo a la función "crearDirectorio"

    Private Function creaDirectorio(ByVal dir As String) As String
        Dim peticionFTP As FtpWebRequest = CType(WebRequest.Create(New Uri(dir)), FtpWebRequest)

Me.configurarFTP(peticionFTP, WebRequestMethods.Ftp.MakeDirectory)

        Try
            Dim respuesta As FtpWebResponse
            respuesta = CType(peticionFTP.GetResponse(), FtpWebResponse)
            respuesta.Close()
            Return String.Empty
        Catch ex As Exception
            Return ex.Message
        End Try
    End Function

Mejoras:

  • Optimizado código usando la función "configurarFTP"

Con esto, casí está lista nuestra mejorada clase para FTP sobre VB.net; faltaría ver los campos de clase:

Dim host, user, pass As String
Dim crearSiNo, useBinary As Boolean
Dim longitudBuffer as Integer

De esta manera, el nuevo constructor sería:

    Public Sub New(ByVal host As String, ByVal user As String, ByVal pass As String)>
        Me.host = host
        Me.user = user
        Me.pass = pass
Me.crearSiNo = True
Me.useBinary = True
Me.longitudBuffer = 2048
    End Sub

Lo último (para rizar el rizo) sería elaborar "setters" para las variables de clase. Esta parte la omitiremos de código, pero es esencial que estén presentes para poder dar la funcionalidad total a la clase.

Veamos, una vez más, cómo usar esta clase para usar FTP:

Dim ftp As New Ftp("miweb.com", "usuario", "pass")

ftp.bajarArchivo("archivo/a/bajar", "ruta/a/bajar")
ftp.subirFichero("fichero/a/subir", "ftp://miweb.com/", "carpeta/de/destino")

Como último aspecto a destacar, comentar una "ayuda" que nos brinda VB.net: el objeto "Environment".

Éste tiene un método llamado "GetEnvironmentVariable", al que le pasamos un String, y retorna el valor de dicha variable de entorno.

Es decir, para aquellos que no estén familiarizados con las variables de entorno:

Las variables de entorno son aquellas que están "grabadas" en el sistema, y permiten crear "atajos", configurar aplicaciones, y diversas cosas más.

Ejemplos de algunas varialbes de entorno son TEMP, TMP, APPDATA, USERPROFILE... que contienen rutas a carpetas de sistema (o carpetas útiles).

De esta manera, a la hora de realizar una instalación, nos podemos referir a estas rutas mediante su variable de entorno, haciéndolas "relativas".

Con esto, podemos referirnos a la carpeta de sistema, o a la ruta donde se instalan los programas por defecto (APPDATA), sin tener que escribirla a mano (puede que un usuario tenga los programas en otra carpeta, o incluso en otra unidad del sistema).

Así, usando esta facilidad, podremos acceder a los ficheros de una manera más "universal".

Espero que os sea útil la entrada. Recordad que yo solo os brindo conocimiento (¡cómo lo uséis es cosa vuestra!)

Como siempre digo,

¡Hasta la próxima!