Algoritmo Huffman de compresion y descompresion

Attribute VB_Name = “HuffmanCoding”
‘################################################################
‘ Huffman Coding Compression / Decompression Algorithm
‘ Created 1 August 2000 by James Vincent Carnicelli

‘ NOTES

‘ The Huffman algorithm, named after its inventor, was created
‘ around about 1952.  It’s the method used by most commercial
‘ compression utilities, like PKZIP, and even by the JPEG image
‘ file format.  It’s generally thought to offer an average of
‘ 50% compression, given a typical mix of text and binary data.
‘ For long strings that contain lots of repeating characters or
‘ only a handful of different characters, the compression ratio
‘ could get as high as 80%.  While efficient, this algorithm is
‘ not guaranteed to result in a compressed string that is
‘ smaller than the original source.

‘ This is a less-than-optimal implementation of this compression
‘ algorithm.  It’s very simple to use in your programs (even if
‘ it is difficult to understand how it works).  You need only
‘ call:

‘         Compressed = HuffmanEncode(SourceText, [Force])

‘ passing in the text you want compressed.  If the compressed
‘ version is actually larger than the original source, this
‘ algorithm spits out a special string that contains a four-
‘ byte header and the original source string, so the resulting
‘ string will always be at most four bytes larger than the
‘ source string.  If you pass in True for Force, the result will
‘ always be huffman-encoded, bypassing this neat optimization.
‘ Be aware that the output is binary data, so it might not work
‘ nicely with some things like text boxes, certain Windows
‘ API calls, and some SQL and database field formats.

‘ To decode a string encoded with HuffmanEncode, simply call
‘ the following:

‘         UncompressedText = HuffmanDecode(Compressed)

‘ One cool application of this algorithm is encryption.  Because
‘ Huffman coding relies on variable-bit-length character
‘ representations, it’s next to impossible to decrypt a string
‘ compressed with this algorithm without recognizing the
‘ lookup tables in the header as the key to decrypting it.  You
‘ could even strip out this lookup table and keep it as a
‘ private key to be shared only with those you want.  Without
‘ the lookup table, even someone equiped with this very code
‘ would not likely be able to decrypt the string.

‘ One last thing.  While I’ve tested this algorithm with plain
‘ text strings and even some binary files, I don’t know how
‘ much data you can cram into the compression engine before it
‘ breaks.  With luck, it’s something like 2GB.  In that case,
‘ though, this would be pretty slow.  Also, I have not proven
‘ beyond a doubt that this won’t choke on some data, so I would
‘ encourage you to do so to your satisfaction before putting
‘ this into full production.  Be sure to let me know if you find
‘ anything interesting.
‘################################################################

Option Explicit

Private Enum HuffmanTreeNodeParts
htnWeight = 1
htnIsLeaf = 2
htnAsciiCode = 3
htnBitCode = 4
htnLeftSubtree = 5
htnRightSubtree = 6
End Enum

‘Compress the text.
Public Function HuffmanEncode(Text As String, Optional Force As Boolean) As String
Dim TextLen As Long, Char As Byte, i As Long, j As Long
Dim CodeCounts(255) As Long, BitStrings(255), BitString
Dim HuffmanTrees As Collection
Dim HTRootNode As Collection, HTNode As Collection
Dim NextByte As Byte, BitPos As Integer, Temp As String

‘Initialize for processing.
TextLen = Len(Text)
Set HuffmanTrees = New Collection

‘Is there anything to encode?
If TextLen = 0 Then
HuffmanEncode = “HE0″ & vbCr  ‘Version 0 = Plain text
Exit Function  ‘No point in continuing
End If

HuffmanEncode = “HE2″ & vbCr  ‘Version 1

‘Count how many times each ASCII code is encountered in text.
For i = 1 To TextLen
Char = Asc(Mid(Text, i, 1))
CodeCounts(Char) = CodeCounts(Char) + 1
Next

‘Initialize the forest of Huffman trees; one for each ASCII
‘character used.
For i = 0 To UBound(CodeCounts)
If CodeCounts(i) > 0 Then
Set HTNode = NewNode
S HTNode, htnAsciiCode, Chr(i)
S HTNode, htnWeight, CDbl(CodeCounts(i) / TextLen)
S HTNode, htnIsLeaf, True

‘Now place it in its reverse-ordered position.
For j = 1 To HuffmanTrees.Count + 1
If j > HuffmanTrees.Count Then
HuffmanTrees.Add HTNode
Exit For
End If
If HTNode(htnWeight) >= HuffmanTrees(j)(htnWeight) Then
HuffmanTrees.Add HTNode, , j
Exit For
End If
Next
End If
Next

‘Now assemble all these single-level Huffman trees into
‘one single tree, where all the leaves have the ASCII codes
‘associated with them.
If HuffmanTrees.Count = 1 Then
Set HTNode = NewNode
S HTNode, htnLeftSubtree, HuffmanTrees(1)
S HTNode, htnWeight, 1
HuffmanTrees.Remove (1)
HuffmanTrees.Add HTNode
End If
While HuffmanTrees.Count > 1
Set HTNode = NewNode
S HTNode, htnRightSubtree, HuffmanTrees(HuffmanTrees.Count)
HuffmanTrees.Remove HuffmanTrees.Count
S HTNode, htnLeftSubtree, HuffmanTrees(HuffmanTrees.Count)
HuffmanTrees.Remove HuffmanTrees.Count
S HTNode, htnWeight, HTNode(htnLeftSubtree)(htnWeight) + HTNode(htnRightSubtree)(htnWeight)

‘Place this new tree it in its reverse-ordered position.
For j = 1 To HuffmanTrees.Count + 1
If j > HuffmanTrees.Count Then
HuffmanTrees.Add HTNode
Exit For
End If
If HTNode(htnWeight) >= HuffmanTrees(j)(htnWeight) Then
HuffmanTrees.Add HTNode, , j
Exit For
End If
Next
Wend
Set HTRootNode = HuffmanTrees(1)
AttachBitCodes BitStrings, HTRootNode, Array()
For i = 0 To UBound(BitStrings)
If Not IsEmpty(BitStrings(i)) Then
Set HTNode = BitStrings(i)
Temp = Temp & HTNode(htnAsciiCode) _
& BitsToString(HTNode(htnBitCode))
End If
Next
HuffmanEncode = HuffmanEncode & Len(Temp) & vbCr & Temp

‘The next part of the header is a checksum value, which
‘we’ll use later to verify our decompression.
Char = 0
For i = 1 To TextLen
Char = Char Xor Asc(Mid(Text, i, 1))
Next
HuffmanEncode = HuffmanEncode & Chr(Char)

‘The final part of the header identifies how many bytes
‘the original text strings contains.  We will probably
‘have a few unused bits in the last byte that we need to
‘account for.  Additionally, this serves as a final check
‘for corruption.
HuffmanEncode = HuffmanEncode & TextLen & vbCr

‘Now we can encode the data by exchanging each ASCII byte for
‘its appropriate bit string.
BitPos = -1
Char = 0
Temp = “”
For i = 1 To TextLen
BitString = BitStrings(Asc(Mid(Text, i, 1)))(htnBitCode)
‘Add each bit to the end of the output stream’s 1-byte buffer.
For j = 0 To UBound(BitString)
BitPos = BitPos + 1
If BitString(j) = 1 Then
Char = Char + 2 ^ BitPos
End If
‘If the bit buffer is full, dump it to the output stream.
If BitPos >= 7 Then
Temp = Temp & Chr(Char)
‘If the temporary output buffer is full, dump it
‘to the final output stream.
If Len(Temp) > 1024 Then
HuffmanEncode = HuffmanEncode & Temp
Temp = “”
End If
BitPos = -1
Char = 0
End If
Next
Next
If BitPos > -1 Then
Temp = Temp & Chr(Char)
End If
If Len(Temp) > 0 Then
HuffmanEncode = HuffmanEncode & Temp
End If

‘If it takes up more space compressed because the source is
’small and the header is big, we’ll leave it uncompressed
‘and prepend it with a 4 byte header.
If Len(HuffmanEncode) > TextLen And Not Force Then
HuffmanEncode = “HE0″ & vbCr & Text
End If
End Function

‘Decompress the string back into its original text.
Public Function HuffmanDecode(ByVal Text As String) As String
Dim Pos As Long, Temp As String, Char As Byte, Bits
Dim i As Long, j As Long, CharsFound As Long, BitPos As Integer
Dim CheckSum As Byte, SourceLen As Long, TextLen As Long
Dim HTRootNode As Collection, HTNode As Collection

‘If this was left uncompressed, this will be easy.
If Left(Text, 4) = “HE0″ & vbCr Then
HuffmanDecode = Mid(Text, 5)
Exit Function
End If

‘If this is any version other than 2, we’ll bow out.
If Left(Text, 4) <> “HE2″ & vbCr Then
Err.Raise vbObjectError, “HuffmanDecode()”, _
“The data either was not compressed with HE2 or is corrupt”
End If
Text = Mid(Text, 5)

‘Extract the ASCII character bit-code table’s byte length.
Pos = InStr(1, Text, vbCr)
If Pos = 0 Then
Err.Raise vbObjectError, “HuffmanDecode()”, _
“The data either was not compressed with HE2 or is corrupt”
End If
On Error Resume Next
TextLen = Left(Text, Pos - 1)
If Err.Number <> 0 Then
On Error GoTo 0
Err.Raise vbObjectError, “HuffmanDecode()”, _
“The header is corrupt”
End If
On Error GoTo 0
Text = Mid(Text, Pos + 1)
Temp = Left(Text, TextLen)
Text = Mid(Text, TextLen + 1)
‘Now extract the ASCII character bit-code table.
Set HTRootNode = NewNode
Pos = 1
While Pos <= Len(Temp)
Char = Asc(Mid(Temp, Pos, 1))
Pos = Pos + 1
Bits = StringToBits(Pos, Temp)
Set HTNode = HTRootNode
For j = 0 To UBound(Bits)
If Bits(j) = 1 Then
If HTNode(htnLeftSubtree) Is Nothing Then
S HTNode, htnLeftSubtree, NewNode
End If
Set HTNode = HTNode(htnLeftSubtree)
Else
If HTNode(htnRightSubtree) Is Nothing Then
S HTNode, htnRightSubtree, NewNode
End If
Set HTNode = HTNode(htnRightSubtree)
End If
Next
S HTNode, htnIsLeaf, True
S HTNode, htnAsciiCode, Chr(Char)
S HTNode, htnBitCode, Bits
Wend

‘Extract the checksum.
CheckSum = Asc(Left(Text, 1))
Text = Mid(Text, 2)

‘Extract the length of the original string.
Pos = InStr(1, Text, vbCr)
If Pos = 0 Then
Err.Raise vbObjectError, “HuffmanDecode()”, _
“The header is corrupt”
End If
On Error Resume Next
SourceLen = Left(Text, Pos - 1)
If Err.Number <> 0 Then
On Error GoTo 0
Err.Raise vbObjectError, “HuffmanDecode()”, _
“The header is corrupt”
End If
On Error GoTo 0
Text = Mid(Text, Pos + 1)
TextLen = Len(Text)

‘Now that we’ve processed the header, let’s decode the actual data.
i = 1
BitPos = -1
Set HTNode = HTRootNode
Temp = “”
While CharsFound < SourceLen
If BitPos = -1 Then
If i > TextLen Then
Err.Raise vbObjectError, “HuffmanDecode()”, _
“Expecting more bytes in data stream”
End If
Char = Asc(Mid(Text, i, 1))
i = i + 1
End If
BitPos = BitPos + 1

If (Char And 2 ^ BitPos) > 0 Then
Set HTNode = HTNode(htnLeftSubtree)
Else
Set HTNode = HTNode(htnRightSubtree)
End If
If HTNode Is Nothing Then
‘Uh oh.  We’ve followed the tree to a Huffman tree to a dead
‘end, which won’t happen unless the data is corrupt.
Err.Raise vbObjectError, “HuffmanDecode()”, _
“The header (lookup table) is corrupt”
End If

If HTNode(htnIsLeaf) Then
Temp = Temp & HTNode(htnAsciiCode)
If Len(Temp) > 1024 Then
HuffmanDecode = HuffmanDecode & Temp
Temp = “”
End If
CharsFound = CharsFound + 1
Set HTNode = HTRootNode
End If

If BitPos >= 7 Then BitPos = -1
Wend
If Len(Temp) > 0 Then
HuffmanDecode = HuffmanDecode & Temp
End If
If i <= TextLen Then
Err.Raise vbObjectError, “HuffmanDecode()”, _
“Found extra bytes at end of data stream”
End If

‘Verify data to check for corruption.
If Len(HuffmanDecode) <> SourceLen Then
Err.Raise vbObjectError, “HuffmanDecode()”, _
“Data corrupt because check sums do not match”
End If
Char = 0
For i = 1 To SourceLen
Char = Char Xor Asc(Mid(HuffmanDecode, i, 1))
Next
If Char <> CheckSum Then
Err.Raise vbObjectError, “HuffmanDecode()”, _
“Data corrupt because check sums do not match”
End If
End Function

‘—————————————————————-
‘ Everything below here is only for supporting the two main
‘ routines above.
‘—————————————————————-

‘Follows the tree, now built, to its end leaf nodes, where the
‘character codes are, in order to tell those character codes
‘what their bit string representations are.
Private Sub AttachBitCodes(BitStrings, HTNode As Collection, ByVal Bits)
If HTNode Is Nothing Then Exit Sub
If HTNode(htnIsLeaf) Then
S HTNode, htnBitCode, Bits
Set BitStrings(Asc(HTNode(htnAsciiCode))) = HTNode
Else
ReDim Preserve Bits(UBound(Bits) + 1)
Bits(UBound(Bits)) = 1
AttachBitCodes BitStrings, HTNode(htnLeftSubtree), Bits
Bits(UBound(Bits)) = 0
AttachBitCodes BitStrings, HTNode(htnRightSubtree), Bits
End If
End Sub

‘Turns a string of ‘0′ and ‘1′ characters into a string of bytes
‘containing the bits, preceeded by 1 byte indicating the
‘number of bits represented.
Private Function BitsToString(Bits) As String
Dim Char As Byte, i As Long
BitsToString = Chr(UBound(Bits) + 1)  ‘Number of bits
For i = 0 To UBound(Bits)
If i Mod 8 = 0 Then
If i > 0 Then BitsToString = BitsToString & Chr(Char)
Char = 0
End If
If Bits(i) = 1 Then  ‘Bit value = 1
‘Mask the bit into its proper position in the byte
Char = Char + 2 ^ (i Mod 8)
End If
Next
BitsToString = BitsToString & Chr(Char)
End Function

‘The opposite of BitsToString() function.
Private Function StringToBits(StartPos As Long, Bytes As String)
Dim Char As Byte, i As Long, BitCount As Long, Bits
Bits = Array()
BitCount = Asc(Mid(Bytes, StartPos, 1))
StartPos = StartPos + 1
For i = 0 To BitCount - 1
If i Mod 8 = 0 Then
Char = Asc(Mid(Bytes, StartPos, 1))
StartPos = StartPos + 1
End If
ReDim Preserve Bits(UBound(Bits) + 1)
If (Char And 2 ^ (i Mod 8)) > 0 Then   ‘Bit value = 1
Bits(UBound(Bits)) = 1
Else  ‘Bit value = 0
Bits(UBound(Bits)) = 0
End If
Next
StringToBits = Bits
End Function

‘Remove the specified item and put the specified value in its place.
Private Sub S(Col As Collection, Index As HuffmanTreeNodeParts, Value)
Col.Remove Index
If Index > Col.Count Then
Col.Add Value
Else
Col.Add Value, , Index
End If
End Sub

‘Creates a new Huffman tree node with the default values set.
Private Function NewNode() As Collection
Dim Node As New Collection
Node.Add 0  ‘htnWeight
Node.Add False  ‘htnIsLeaf
Node.Add Chr(0)  ‘htnAsciiCode
Node.Add “”  ‘htnBitCode
Node.Add Nothing  ‘htnLeftSubtree
Node.Add Nothing  ‘htnRightSubtree
Set NewNode = Node
End Function

Rutinas de manipulación de bits

KB de Microsoft. Article ID: Q185554

Rutinas :

- BitMask : devuelve una máscara empleada por las otras rutinas.
- BitSet : Pone un bit a uno o a cero.
- BitFlip : Cambia el estado de un bit.
- BitTest : Devuelve el estado de un bit.
- ArrayBitSet : Pone un bit a uno o a cero.
- ArrayBitFlip : Cambia el estado de un bit.
- ArrayBitTest : Devuelve el estado de un bit.

Las rutinas Array asumen 32 bits por elemento, comenzando con los bits del 0 al 31 en el primero, del 32 al 63 en el segundo, etc. La matriz no necesita comenzar en el elemento cero.

Estas rutinas tienen uno a más de los siguientes parámetros :
- X : un long que contiene los 32 bits a manipular.
- A() : una matriz conteniendo los bits a manipular.
- Value : TRUE para poner un bit a uno y FALSE para ponerlo a cero.
- N : número que indica el bit a manipular. En el caso de una variable long va desde el 0 al 31. En el caso de una matriz va desde el 0 hasta el (numero de elementos * 32) -1. Si el valor de N está fuera del rango se ignora. En el caso de una función se devuelve FALSE.

Las funciones BitTest y ArrayBitTest devuelven TRUE (-1) si el bit está a 1 y FALSE (0) si está a 0.

Codigo:

Function BitMask(ByVal N As Long) As Long Dim I As Long, Mask As Long If N < 0 Or N > 31 Then BitMask = 0 ElseIf N = 31 Then BitMask = &H80000000 Else: Mask = 1 For I = 1 To N Mask = Mask + Mask Next I BitMask = Mask End If End Function Sub BitSet(X As Long, ByVal N As Long, ByVal Value As Boolean) If Value Then X = X Or BitMask(N) Else: X = X And Not BitMask(N) End If End Sub Sub BitFlip(X As Long, ByVal N As Long) X = X Xor BitMask(N) End Sub Function BitTest(X As Long, ByVal N As Long) As Boolean ` Return False if invalid N BitTest = (X And BitMask(N)) <> 0 End Function Sub ArrayBitSet(A() As Long, ByVal N As Long, ByVal Value As Boolean) Dim Element As Integer Element = N \ 32 + LBound(A) If Element <= UBound(A) And N >= 0 Then BitSet A(Element), N Mod 32, Value End If End Sub Sub ArrayBitFlip(A() As Long, ByVal N As Long) Dim Element As Integer Element = N \ 32 + LBound(A) If Element <= UBound(A) And N >= 0 Then BitFlip A(Element), N Mod 32 End If End Sub Function ArrayBitTest(A() As Long, ByVal N As Long) As Boolean ` Returns False if invalid N. Dim Element As Integer Element = N \ 32 + LBound(A) If Element <= UBound(A) And N >= 0 Then ArrayBitTest = BitTest(A(Element), N Mod 32) Else ArrayBitTest = False End If End Function

Basándonos en estas rutinas de Microsoft es sencillo crearnos una para desplazar bits a la izquierda y a la derecha, similar a los operadores << y >> de C.

Codigo:

Function DesplazaLongIzda(ByVal numero As Long, pos As Long) As Long Dim i As Long For i = 31 To pos Step -1 BitSet numero, i, BitTest(numero, i - pos) Next i For i = pos - 1 To 0 Step -1 BitSet numero, i, False Next i DesplazaLongIzda = numero End Function Function DesplazaLongDcha(ByVal numero As Long, pos As Long) As Long Dim i As Long For i = 0 To 31 - pos BitSet numero, i, BitTest(numero, i + pos) Next i For i = 31 - pos To 31 BitSet numero, i, False Next i DesplazaLongDcha = numero End Function
Escrito en C++, Visual Basic. Etiquetas: . No hay comentarios »

Ejemplo de Microsoft Transfer Control; Cliente FTP en VB

Ejemplo que permite descargar y subir archivos a un Ftp utilizando el control Microsoft Transfer controlEl ejemplo es como muestra el gráfico:

vista del formulario de ejemplo para bajar y subir archivos de un servidor ftp

Para agregar el Control Inet, seleccionar desde la ficha componentes Microsoft Internet Transfer control

vista de la ventana de componentes de visual basic para insertar al proyecto el control Inet

También agregar los siguiente controles:

  • Un control commonDialog llamado CommonDialog1 para seleccionar la ruta del archivo a subir y también para descargar
  • Cinco controles TextBox: txt_servidor, txt_Usuario, txt_Pass, txt_local y txt_Remoto: Esto es para el nombre del servidor ftp, para el nombre de Usuario y contraseña del Login y para las rutas de los archivos.
  • Un control ListBox llamado List1 para ver el estado.
  • Cuatro CommandButton: El Command1 para Subir un fichero, Command2 para descargar, Command3 para seleccionar un fichero del disco a subir, y Command4 para seleccionar ruta y nombre del archivo a descargar a disco.

Código fuente en un formulario:

Option Explicit

Private Sub Mostrar_Estado_FTP(ByVal estado As String)

    List1.AddItem estado

    List1.ListIndex = List1.ListCount - 1

End Sub

Private Sub Command2_Click()

Dim El_Host As String

If txt_Remoto = "" Then

       MsgBox " No hay archivo para descargar", vbInformation

      Exit Sub

    End If

List1.AddItem " ..Descargando "

        'Asigna la Url, es decir el nombre del Host FTP

        El_Host = "ftp://" & txt_servidor

With Inet1

.URL = El_Host

'nombre de usuario y password de la cuanta FTP

        .UserName = txt_Usuario

        .Password = txt_Pass

'DEscarga el archivo indicado con el comando Get

        Call .Execute(, "Get " & txt_Remoto & " " & txt_local)

        DoEvents

End With

End Sub

Private Sub Command1_Click()

Dim El_Host As String

If txt_local = "" Then

       MsgBox " No hay archivo pra subir", vbInformation

      Exit Sub

    End If

List1.AddItem " ..Subiendo archivo "

El_Host = "ftp://" & txt_servidor

With Inet1

'Asigna la Url, es decir el nombre del Host FTP

        .URL = El_Host

'nombre de usuario y password de la cuanta FTP

        .UserName = txt_Usuario

        .Password = txt_Pass

'Escribe el fichero en el servidor con el comando Put

        Call .Execute(, "Put " & txt_local & " " & txt_Remoto)

DoEvents

End With

End Sub

'botón para seleccionar fichero a subir al Ftp

Private Sub Command3_Click()

With CommonDialog1

.DialogTitle = " Seleccione el archivo a subir"

     .ShowOpen

If .FileName = "" Then

        txt_local = ""

        Exit Sub

     Else

        txt_local = .FileName

     End If

End With

End Sub

'botón para seleccionar la ruta y nombre de archivo a descargar

Private Sub Command4_Click()

With CommonDialog1

.DialogTitle = " Seleccione ruta y nombre del archivo a descargar"

     .ShowSave

If .FileName = "" Then

        txt_Remoto = ""

        Exit Sub

     Else

        txt_Remoto = .FileName

     End If

End With

End Sub

Private Sub Inet1_StateChanged(ByVal State As Integer)

Select Case State

'Dependiendo del valor recibido de State _

         muestra en el List1 la información de estado

Case 0: Mostrar_Estado_FTP " Nothing "

        Case 1: Mostrar_Estado_FTP " Resolviendo Host "

        Case 2: Mostrar_Estado_FTP " Host Resuelto "

        Case 3: Mostrar_Estado_FTP " ..Conectando a: " & txt_servidor

        Case 4: Mostrar_Estado_FTP ".. Conectado a " & txt_servidor

        Case 5: Mostrar_Estado_FTP " Petición"

        Case 6: Mostrar_Estado_FTP " ..enviando petición"

        Case 7: Mostrar_Estado_FTP " Recibiendo Respuesta "

        Case 8: Mostrar_Estado_FTP " Respuesta recibida "

        Case 9: Mostrar_Estado_FTP " ..Desconectando "

        Case 10: Mostrar_Estado_FTP " Estado : Desconectado"

        Case 11: Mostrar_Estado_FTP " Error: " & Inet1.ResponseInfo

        Case 12: Mostrar_Estado_FTP Inet1.ResponseInfo

Case Else: Mostrar_Estado_FTP " Estado -> " & Format$(State)

    End Select

DoEvents

End Sub

Private Sub Form_Load()

Command1.Caption = "Subir archivo "

Command2.Caption = " Descargar archivo "

Me.Caption = "Ejemplo del control Inet para descargar y subir ficheros"

End Sub
Escrito en Visual Basic. Etiquetas: . No hay comentarios »

Cliente Ftp con el Api wininet

Este es un programa de ejemplo con el código fuente para acceder a un servidor vía Ftp y poder realizar operaciones básicas utilizando el api de windows


Las opciones principales son:

  • Subir y bajar archivos
  • Eliminar y renombrar carpetas y archivos
  • Listar los mismos en un control ListView (no es nada del otro mundo)
  • algunas otras opciones

El ejemplo utiliza un módulo de clase que tiene las funciones descritas arriba.

Nota , El programa mientras lo hacía, no lo llegué a testear a fondo, solo lo probé con mi servidor Ftp y no me surgió errores, con lo cual no quiere decir que no los tenga, en todo caso si le encontrás alguno y querés avisarme, enviame un mail para ver si lo puedo ojear y poder corregirlo :)

Imágen del ejemplo:

vista del formulario con el control Listview que lista los archivos remotos del FTP y los demás comandos del programa

Código fuente del módulo de clase:

  1. Private Const MAX_PATH = 260
  2. ‘Constante para el atributo de directorio
  3. Private Const FILE_ATTRIBUTE_DIRECTORY = &H10
  4. ‘— tipos de archivos — para el Upload y Download
  5. Private Const FTP_TRANSFER_TYPE_UNKNOWN = &H0
  6. Private Const FTP_TRANSFER_TYPE_ASCII = &H1
  7. Private Const FTP_TRANSFER_TYPE_BINARY = &H2
  8. ‘Puerto FTP
  9. Private Const INTERNET_DEFAULT_FTP_PORT = 21
  10. Private Const INTERNET_SERVICE_FTP = 1
  11. ‘ Modo de conexión FTP
  12. Private Const INTERNET_FLAG_PASSIVE = &H8000000
  13. Private Const PassiveConnection As Boolean = True
  14. ‘— formas de entrar en internet —
  15. ‘ usa config del registro
  16. Private Const INTERNET_OPEN_TYPE_PRECONFIG = 0
  17. ‘ directo a internet
  18. Private Const INTERNET_OPEN_TYPE_DIRECT = 1
  19. ‘ via proxy
  20. Private Const INTERNET_OPEN_TYPE_PROXY = 3
  21. Private Const INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY = 4
  22. ‘Type para atributos de fecha y hora de archivos
  23. Private Type FILETIME
  24. dwLowDateTime As Long
  25. dwHighDateTime As Long
  26. End Type
  27. ‘Otros atributos de archivo tamaño, nombre, fecha etc..
  28. Private Type WIN32_FIND_DATA
  29. dwFileAttributes As Long
  30. ftCreationTime As FILETIME
  31. ftLastAccessTime As FILETIME
  32. ftLastWriteTime As FILETIME
  33. nFileSizeHigh As Long
  34. nFileSizeLow As Long
  35. dwReserved0 As Long
  36. dwReserved1 As Long
  37. cFileName As String * MAX_PATH
  38. cAlternate As String * 14
  39. End Type
  40. ‘ Declaraciones Apis
  41. ‘***************************************************************
  42. Private Declare Function InternetCloseHandle _
  43. Lib “wininet.dll” (ByVal hInet As Long) As Integer
  44. ‘Establece una conexión a internet para poder iniciar sesión Ftp
  45. Private Declare Function InternetConnect _
  46. Lib “wininet.dll” Alias “InternetConnectA” _
  47. (ByVal hInternetSession As Long, _
  48. ByVal sServerName As String, _
  49. ByVal nServerPort As Integer, _
  50. ByVal sUserName As String, _
  51. ByVal sPassword As String, _
  52. ByVal lService As Long, ByVal lFlags As Long, _
  53. ByVal lContext As Long) As Long
  54. ‘función api que Conecta al Ftp
  55. Private Declare Function InternetOpen _
  56. Lib “wininet.dll” Alias “InternetOpenA” _
  57. (ByVal sAgent As String, ByVal lAccessType As Long, _
  58. ByVal sProxyName As String, _
  59. ByVal sProxyBypass As String, _
  60. ByVal lFlags As Long) As Long
  61. ‘Función que Establece el path corriente
  62. Private Declare Function FtpSetCurrentDirectory _
  63. Lib “wininet.dll” Alias “FtpSetCurrentDirectoryA” _
  64. (ByVal hFtpSession As Long, _
  65. ByVal lpszDirectory As String) As Boolean
  66. ‘api que Recupera el path donde estamos ubicados
  67. Private Declare Function FtpGetCurrentDirectory _
  68. Lib “wininet.dll” Alias “FtpGetCurrentDirectoryA” _
  69. (ByVal hFtpSession As Long, _
  70. ByVal lpszCurrentDirectory As String, _
  71. lpdwCurrentDirectory As Long) As Long
  72. ‘Api para Crear un directorio
  73. Private Declare Function FtpCreateDirectory _
  74. Lib “wininet.dll” Alias “FtpCreateDirectoryA” _
  75. (ByVal hFtpSession As Long, _
  76. ByVal lpszDirectory As String) As Boolean
  77. ‘Api que Elimina un directorio del FTP
  78. Private Declare Function FtpRemoveDirectory _
  79. Lib “wininet.dll” Alias “FtpRemoveDirectoryA” _
  80. (ByVal hFtpSession As Long, _
  81. ByVal lpszDirectory As String) As Boolean
  82. ‘Para Borrar un fichero
  83. Private Declare Function FtpDeleteFile _
  84. Lib “wininet.dll” Alias “FtpDeleteFileA” _
  85. (ByVal hFtpSession As Long, _
  86. ByVal lpszFileName As String) As Boolean
  87. ‘Función que Renombra un fichero
  88. Private Declare Function FtpRenameFile _
  89. Lib “wininet.dll” Alias “FtpRenameFileA” _
  90. (ByVal hFtpSession As Long, _
  91. ByVal lpszExisting As String, _
  92. ByVal lpszNew As String) As Boolean
  93. ‘Recupera un archivo
  94. Private Declare Function FtpGetFile Lib “wininet.dll” _
  95. Alias “FtpGetFileA” (ByVal hConnect As Long, _
  96. ByVal lpszRemoteFile As String, _
  97. ByVal lpszNewFile As String, ByVal fFailIfExists As Long, _
  98. ByVal dwFlagsAndAttributes As Long, ByVal dwFlags As Long, _
  99. ByRef dwContext As Long) As Boolean
  100. ‘Escribe un archivo
  101. Private Declare Function FtpPutFile Lib “wininet.dll” _
  102. Alias “FtpPutFileA” (ByVal hConnect As Long, _
  103. ByVal lpszLocalFile As String, _
  104. ByVal lpszNewRemoteFile As String, ByVal dwFlags As Long, _
  105. ByVal dwContext As Long) As Boolean
  106. ‘Api Para manejar los errores
  107. Private Declare Function InternetGetLastResponseInfo _
  108. Lib “wininet.dll” Alias “InternetGetLastResponseInfoA” _
  109. (lpdwError As Long, ByVal lpszBuffer As String, _
  110. lpdwBufferLength As Long) As Boolean
  111. ‘Busca el primer archivo de un path
  112. Private Declare Function FtpFindFirstFile Lib “wininet.dll” _
  113. Alias “FtpFindFirstFileA” (ByVal hFtpSession As Long, _
  114. ByVal lpszSearchFile As String, _
  115. lpFindFileData As WIN32_FIND_DATA, _
  116. ByVal dwFlags As Long, ByVal dwContent As Long) As Long
  117. ‘api para buscar el siguiente archivo
  118. Private Declare Function InternetFindNextFile Lib “wininet.dll” _
  119. Alias “InternetFindNextFileA” (ByVal hFind As Long, _
  120. lpvFindData As WIN32_FIND_DATA) As Long
  121. Public Enum e_TipoTransferencia
  122. [ BINARIO ] = FTP_TRANSFER_TYPE_BINARY
  123. [ ASCII ] = FTP_TRANSFER_TYPE_ASCII
  124. [ DESCONOCIDO ] = FTP_TRANSFER_TYPE_UNKNOWN
  125. End Enum
  126. ‘Handle de la conexión Ftp
  127. Dim HandleConect As Long
  128. ‘Handle de la conexión a Internet
  129. Dim hOpen As Long
  130. ‘Variables locales
  131. Private m_DirectorioActual As String
  132. Private m_Usuario As String
  133. Private m_PassWord As String
  134. Private m_Servidor As String
  135. Private m_DirAnterior As String
  136. Private m_listView As ListView
  137. Private m_TipoTransferencia As Long
  138. Private m_form As Form
  139. Private ctrl As Object
  140. ‘Funciones Varias para el manejo de archivos y carpetas en el servidor Ftp
  141. ‘***********************************************************************
  142. ‘***********************************************************************
  143. ‘Rutina que conecta al Servidor Ftp
  144. Public Function ConectarFtp(Optional ControlStatus As Object _
  145. = Nothing) As Boolean
  146. ‘Verificamos que los datos de la cuenta estén establecidas, si no mostramos un _
  147. mensaje y salimos
  148. If m_Usuario = “” Or m_Servidor = “” Or m_PassWord = “” Then
  149. MsgBox “No se puede conectar. Verifique el Nombre de usuario,” _
  150. & “El nombre del Servidor y la contraseña que estén establecidas”, vbCritical
  151. ConectarFtp = False
  152. Exit Function
  153. End If
  154. Set ctrl = ControlStatus
  155. Status “…Intentando conectar a: ” & m_Servidor
  156. m_form.MousePointer = vbHourglass
  157. ‘Abrimos una conección a Internet
  158. hOpen = InternetOpen(vbNullString, _
  159. INTERNET_OPEN_TYPE_PRECONFIG, vbNullString, _
  160. vbNullString, 0)
  161. If hOpen = 0 Then
  162. Status “Error en la conexión a internet, compruebe la conexión”
  163. m_form.MousePointer = vbDefault
  164. ConectarFtp = False
  165. Exit Function
  166. End If
  167. ‘Conectamos al servidor FTP, pasandole los datos: login y servidor
  168. HandleConect = InternetConnect(hOpen, m_Servidor, _
  169. INTERNET_DEFAULT_FTP_PORT, m_Usuario, _
  170. m_PassWord, INTERNET_SERVICE_FTP, _
  171. IIf(PassiveConnection, INTERNET_FLAG_PASSIVE, 0), 0)
  172. If HandleConect = 0 Then
  173. Status “Error. Compruebe los datos del servidor Ftp sin son correctos”
  174. m_form.MousePointer = vbDefault
  175. ConectarFtp = False
  176. Exit Function
  177. End If
  178. Status “Conectado a: ” & m_Servidor
  179. m_form.MousePointer = vbDefault
  180. ConectarFtp = True
  181. End Function
  182. ‘Desconecta del servidor FTP
  183. ‘**************************************************
  184. Public Sub Desconectar()
  185. Dim ret As Long
  186. ‘cierra la conexion FTP
  187. ret = InternetCloseHandle(HandleConect)
  188. If ret = 0 Then Status “Error al desconectar”: Exit Sub
  189. ‘cierra la conexion a internet
  190. ret = InternetCloseHandle(hOpen)
  191. If ret = 0 Then Status “Error al desconectar”: Exit Sub
  192. Status “Desconectado de: ” & m_Servidor
  193. Class_Terminate
  194. End Sub
  195. ‘Recupera el directorio actual donde estamos ubicados
  196. ‘*****************************************************
  197. Public Function GetDirectorioActual() As String
  198. ‘Crea un buffer para el path
  199. m_DirectorioActual = String(MAX_PATH, 0)
  200. ‘Obtenemos el directorio actual
  201. ret = FtpGetCurrentDirectory(HandleConect, m_DirectorioActual, _
  202. Len(m_DirectorioActual))
  203. GetDirectorioActual = m_DirectorioActual
  204. End Function
  205. ‘Establecemos el Directorio Actual
  206. ‘****************************************************
  207. Public Sub CambiarDirectorio(PathDirectorio As String)
  208. Dim pData As WIN32_FIND_DATA
  209. Dim hFind As Long ‘handle usado para buscar fichs en FTP
  210. Dim ret As Long
  211. Dim strDir As String
  212. strDir = Replace(m_DirectorioActual, Chr(0), “”)
  213. If strDir = “/” And PathDirectorio = “../Subir un nivel” Then: Exit Sub
  214. m_form.MousePointer = vbHourglass
  215. If PathDirectorio = “../Subir un nivel” Then
  216. pos = InStrRev(strDir, “/”)
  217. strDir = Left(strDir, pos)
  218. ‘Cambia al Directorio Ftp al directorio especificado
  219. ret = FtpSetCurrentDirectory(HandleConect, strDir)
  220. If ret = 0 Then
  221. Status “Error al cambiar de directorio.”
  222. End If
  223. m_form.MousePointer = vbDefault
  224. Exit Sub
  225. End If
  226. ‘Cambia al Directorio especificado
  227. ret = FtpSetCurrentDirectory(HandleConect, strDir & “/” & PathDirectorio)
  228. If ret = 0 Then
  229. Status “Error al cambiar de directorio”
  230. End If
  231. m_form.MousePointer = vbDefault
  232. End Sub
  233. ‘Crea un nuevo directorio
  234. ‘*******************************************
  235. Public Sub CrearDirectorio(NameDirectorio As String)
  236. ‘Creamos la nueva carpeta
  237. ret = FtpCreateDirectory(HandleConect, NameDirectorio)
  238. If Not ret Then
  239. Status “Error al crear el directorio, compruebe el nombre que sea válido”
  240. Else
  241. m_listView.ListItems.Add , , NameDirectorio, , “carpeta”
  242. m_listView.ListItems(m_listView.ListItems.Count).Selected = True
  243. m_listView.SetFocus
  244. End If
  245. End Sub
  246. ‘Crea o sube un nuevo Archivo.
  247. ‘********************************************
  248. Public Sub SubirArchivo(localArchivo As String, NombreArchivoRemoto As String)
  249. ‘ ( Upload ) Envía el fichero al servidor FTP
  250. ret = FtpPutFile(HandleConect, localArchivo, NombreArchivoRemoto, _
  251. m_TipoTransferencia, 0)
  252. If ret Then
  253. m_listView.ListItems.Add , , NombreArchivoRemoto, , “archivo”
  254. m_listView.ListItems(m_listView.ListItems.Count).Selected = True
  255. m_listView.SetFocus
  256. Else
  257. Status “Error al subir el fichero:” & NombreArchivoRemoto
  258. End If
  259. End Sub
  260. ‘Renombra un archivo en el directorio Ftp corriente
  261. ‘****************************************************
  262. Public Sub RenombrarArchivo(Archivo As String, nuevoNombre As String)
  263. ret = FtpRenameFile(HandleConect, Archivo, nuevoNombre)
  264. If ret Then
  265. m_listView.SelectedItem.Text = nuevoNombre
  266. m_listView.SetFocus
  267. Else
  268. Status “Error al renombrar el fichero:” & nuevoNombre
  269. End If
  270. End Sub
  271. Public Sub ObtenerArchivo(ArchivoRemoto As String, ArchivoLocal As String, _
  272. Optional SobreEscribir As Boolean = False)
  273. ‘recupera fichero del servidor FTP: ArchivoRemoto es el nombre del archivo remoto
  274. ‘ArchivoLocal es el nombre y ruta donde se colocará el archivo en local
  275. ret = FtpGetFile(HandleConect, ArchivoRemoto, ArchivoLocal, _
  276. SobreEscribir, 0, m_TipoTransferencia, 0)
  277. If ret Then
  278. Status “Archivo descargado correctamente:”
  279. m_listView.SetFocus
  280. Else
  281. Status “Error al intentar descargar el fichero: ” & ArchivoRemoto
  282. End If
  283. End Sub
  284. ‘Eliminar Archivo del servidor
  285. Public Sub EliminarArchivo(Archivo As String)
  286. ret = FtpDeleteFile(HandleConect, Archivo)
  287. If Not ret Then
  288. Status “Error. No se pudo eliminar el archivo: ” & Archivo
  289. End If
  290. End Sub
  291. ‘Elimina un directorio
  292. ‘***********************************************************************
  293. Public Sub EliminarDirectorio(Directorio As String)
  294. ‘elimina el directorio
  295. ret = FtpRemoveDirectory(HandleConect, Directorio)
  296. If Not ret Then
  297. Status “Error. No se pudo eliminar el Directorio: ” & Directorio
  298. End If
  299. End Sub
  300. Private Sub Status(mensaje As String)
  301. On Error GoTo SubError
  302. ctrl = mensaje
  303. Exit Sub
  304. SubError:
  305. If Err.Number = 91 Then Resume Next
  306. End Sub
  307. ‘Lista los archivos el el LV
  308. ‘***********************************************************************
  309. Public Sub ListarArchivos()
  310. Dim pData As WIN32_FIND_DATA
  311. Dim hFind As Long ‘handle usado para buscar fichs en FTP
  312. Dim ret As Long ‘valor devuelto por API
  313. m_form.MousePointer = vbHourglass
  314. ‘crea un buffer
  315. pData.cFileName = String(MAX_PATH, 0)
  316. ‘busca el primer fichero
  317. hFind = FtpFindFirstFile(HandleConect, “*.*”, pData, 0, 0)
  318. m_listView.ListItems.Clear
  319. ‘Si Hfind vale 0 es porque no hay archivos ni directorios
  320. If hFind = 0 Then
  321. m_listView.ListItems.Add , , “../Subir un nivel”, , “carpeta”
  322. m_form.MousePointer = vbDefault
  323. Exit Sub
  324. End If
  325. m_listView.ListItems.Add , , “../Subir un nivel”, , “carpeta”
  326. If pData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY Then
  327. m_listView.ListItems.Add , , pData.cFileName, , “carpeta”
  328. Else
  329. m_listView.ListItems.Add(, , pData.cFileName, , “archivo”).SubItems(1) = _
  330. Round((pData.nFileSizeLow / 1024), 2) & ” Kb”
  331. End If
  332. ’si no hay mas Archivos sale
  333. If hFind = 0 Then
  334. m_form.MousePointer = vbDefault
  335. Exit Sub
  336. End If
  337. Do
  338. ‘crea un buffer
  339. pData.cFileName = String(MAX_PATH, 0) ’se llena con nulos
  340. ‘ busca el siguiente archivo
  341. ret = InternetFindNextFile(hFind, pData)
  342. ’si no hay ficheros, no sigue
  343. If ret = 0 Then Exit Do
  344. If pData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY Or _
  345. pData.dwFileAttributes = 0 Then
  346. ‘Agrega el nombre del directorio al control ListView
  347. m_listView.ListItems.Add , , pData.cFileName, , “carpeta”
  348. Else
  349. ‘agrega el archivo y Muestra el tamaño del mismo
  350. m_listView.ListItems.Add(, , pData.cFileName, , “archivo”).SubItems(1) = _
  351. Round((pData.nFileSizeLow / 1024), 2) & ” Kb”
  352. End If
  353. Loop
  354. ‘Cerramos el handle de búsqueda anterior
  355. InternetCloseHandle hFind
  356. m_listView.Sorted = True
  357. m_form.MousePointer = vbDefault
  358. End Sub
  359. ‘Actualiza la lista de Archivos y directorios en el ListView
  360. ‘************************************************************
  361. Public Sub Actualizar()
  362. Dim pData As WIN32_FIND_DATA
  363. Dim hFind As Long ‘handle usado para buscar fichs en FTP
  364. Dim ret As Long ‘valor devuelto por API
  365. m_form.MousePointer = vbHourglass
  366. ‘crea un buffer
  367. pData.cFileName = String(MAX_PATH, 0)
  368. ‘busca el primer fichero
  369. hFind = FtpFindFirstFile(HandleConect, “*.*”, pData, 0, 0)
  370. m_listView.ListItems.Clear
  371. If hFind = 0 Then
  372. m_listView.ListItems.Add , , “../Subir un nivel”, , “carpeta”
  373. m_form.MousePointer = vbDefault
  374. Exit Sub
  375. End If
  376. m_listView.ListItems.Add , , “../Subir un nivel”, , “carpeta”
  377. If pData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY Then
  378. m_listView.ListItems.Add , , pData.cFileName, , “carpeta”
  379. Else
  380. m_listView.ListItems.Add(, , pData.cFileName, , “archivo”).SubItems(1) = _
  381. Round((pData.nFileSizeLow / 1024), 2) & ” Kb”
  382. End If
  383. ’si no hay mas Archivos sale
  384. If hFind = 0 Then
  385. m_form.MousePointer = vbDefault
  386. Exit Sub
  387. End If
  388. Do
  389. ‘buffer
  390. pData.cFileName = String(MAX_PATH, 0) ’se llena con nulos
  391. ‘ Busca el siguiente File
  392. ret = InternetFindNextFile(hFind, pData)
  393. ’si no hay mas arhivos, no sigue buscando
  394. If ret = 0 Then Exit Do
  395. ‘Archivo
  396. If pData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY Or pData.dwFileAttributes = 0 Then
  397. m_listView.ListItems.Add , , pData.cFileName, , “carpeta”
  398. m_listView.ListItems.Add(, , pData.cFileName, , “carpeta”).ListSubItems(0) = _
  399. pData.ftCreationTime.dwHighDateTime
  400. Else
  401. m_listView.ListItems.Add(, , pData.cFileName, , “archivo”).SubItems(1) = _
  402. Round((pData.nFileSizeLow / 1024), 2) & ” Kb”
  403. End If
  404. Loop
  405. ‘Cerramos el handle de búsqueda
  406. InternetCloseHandle hFind
  407. m_listView.Sorted = True
  408. m_form.MousePointer = vbDefault
  409. End Sub
  410. Public Sub Inicializar(Formulario As Form)
  411. Set m_form = Formulario
  412. End Sub
  413. ‘Para los errores
  414. ‘************************************************+
  415. Private Sub ShowError()
  416. Dim lngNumError As Long
  417. Dim strMemoError As String
  418. Dim lngTamBuffer As Long
  419. ‘—————————–
  420. ‘Tamaño del buffer
  421. InternetGetLastResponseInfo lngNumError, _
  422. strMemoError, lngTamBuffer
  423. ‘crea buffer
  424. strMemoError = String(lngTamBuffer, 0)
  425. ‘Recupera informacion del error
  426. InternetGetLastResponseInfo lngNumError, _
  427. strMemoError, lngTamBuffer
  428. ‘Mostrar el error en un msgbox
  429. MsgBox “Error ” & CStr(lngNumError) & “: ” & strMemoError, _
  430. vbOKOnly Or vbCritical
  431. End Sub
  432. ‘Nombre de usuario de la cuenta Ftp
  433. ‘**********************************
  434. Public Property Get Usuario() As String
  435. Usuario = m_Usuario
  436. End Property
  437. Public Property Let Usuario(ByVal vNewValue As String)
  438. m_Usuario = vNewValue
  439. End Property
  440. ‘Nombre del servidor Ftp
  441. ‘***********************
  442. Public Property Get Servidor() As String
  443. Servidor = m_Servidor
  444. End Property
  445. Public Property Let Servidor(ByVal vNewValue As String)
  446. m_Servidor = vNewValue
  447. End Property
  448. ‘Contraseña de la cuenta FTP
  449. ‘***************************
  450. Public Property Get PassWord() As String
  451. PassWord = m_PassWord
  452. End Property
  453. Public Property Let PassWord(ByVal vNewValue As String)
  454. m_PassWord = vNewValue
  455. End Property
  456. ‘Establece el ListView donde listar los ficheros
  457. ‘***********************************************
  458. Public Property Get ListView() As ListView
  459. Set ListView = m_listView
  460. End Property
  461. Public Property Set ListView(ByVal vNewValue As ListView)
  462. Set m_listView = vNewValue
  463. End Property
  464. ‘Propiedad para el Modo de Transferencia que se va a utilizar
  465. ‘***************************************************************
  466. Public Property Get TipoTransferencia() As e_TipoTransferencia
  467. TipoTransferencia = m_TipoTransferencia
  468. End Property
  469. Public Property Let TipoTransferencia(NewData As e_TipoTransferencia)
  470. m_TipoTransferencia = NewData
  471. End Property
  472. Private Sub Class_Terminate()
  473. On Local Error Resume Next
  474. ‘Cerramos la sesión FTP y la conexión a internet
  475. InternetCloseHandle HandleConect
  476. InternetCloseHandle hOpen
  477. ‘Eliminamos las variables de objeto (Listview - Form )
  478. Set ctrl = Nothing
  479. Set ListView = Nothing
  480. Set m_form = Nothing
  481. End Sub
Escrito en Visual Basic. Etiquetas: . 2 Comentarios »