Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

VBA to get list of files on ftp server 3

Status
Not open for further replies.

DevelopV

Technical User
Mar 16, 2012
113
ZA
In Access 2010 I need to get a list of all files in a specific directory on an ftp server and download them to my hard drive.
I never know how many files, or their file names, there are on the ftp server at time of download.

What is the best way to accomplish this?

Many thanks
 
I use this..
Main Windows API declarations for wininet.dll
Code:
' Set Constants
Const FTP_TRANSFER_TYPE_ASCII = &H1
Const FTP_TRANSFER_TYPE_BINARY = &H2
Const INTERNET_DEFAULT_FTP_PORT = 21
Const INTERNET_DEFAULT_HTTP_PORT = 80
Const INTERNET_SERVICE_FTP = 1
Const INTERNET_SERVICE_HTTP = 80
Const INTERNET_FLAG_PASSIVE = &H8000000
Const GENERIC_WRITE = &H40000000
Const GENERIC_READ = &H80000000
Const BUFFER_SIZE = 100
Const PassiveConnection As Boolean = True
Const MAX_PATH = 260

Public Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type

Public Type WIN32_FIND_DATA
    dwFileAttributes As Long
    ftCreationTime As FILETIME
    ftLastAccessTime As FILETIME
    ftLastWriteTime As FILETIME
    nFileSizeHigh As Long
    nFileSizeLow As Long
    dwReserved0 As Long
    dwReserved1 As Long
    cFileName As String * MAX_PATH
    cAlternate As String * 14
End Type

' Declare wininet.dll API Functions
Public Declare Function FtpSetCurrentDirectory Lib "wininet.dll" Alias "FtpSetCurrentDirectoryA" _
    (ByVal hFtpSession As Long, ByVal lpszDirectory As String) As Boolean

Public Declare Function FtpGetCurrentDirectory Lib "wininet.dll" Alias "FtpGetCurrentDirectoryA" _
   (ByVal hFtpSession As Long, ByVal lpszCurrentDirectory As String, lpdwCurrentDirectory As Long) As Boolean
   
Public Declare Function FtpGetFileSize Lib "wininet.dll" _
(ByVal hFile As Long, ByRef lpdwFileSizeHigh As Long) As Long
   
Public Declare Function InternetWriteFile Lib "wininet.dll" _
(ByVal hFile As Long, ByRef sBuffer As Byte, ByVal lNumBytesToWite As Long, _
dwNumberOfBytesWritten As Long) As Integer

Declare Function InternetReadFile Lib "wininet.dll" _
(ByVal hFile As Long, ByRef sBuffer As Byte, ByVal lNumBytesToRead As Long, _
dwNumberOfBytesRead As Long) As Integer

Public Declare Function FtpOpenFile Lib "wininet.dll" Alias "FtpOpenFileA" _
(ByVal hFtpSession As Long, ByVal sBuff As String, ByVal Access As Long, ByVal Flags As Long, ByVal Context As Long) As Long

Public Declare Function FtpPutFile Lib "wininet.dll" Alias "FtpPutFileA" _
(ByVal hFtpSession As Long, ByVal lpszLocalFile As String, _
      ByVal lpszRemoteFile As String, _
      ByVal dwFlags As Long, ByVal dwContext As Long) As Boolean
      
Public Declare Function FtpDeleteFile Lib "wininet.dll" _
    Alias "FtpDeleteFileA" (ByVal hFtpSession As Long, _
    ByVal lpszFileName As String) As Boolean
Public Declare Function InternetCloseHandle Lib "wininet.dll" _
(ByVal hInet As Long) As Long

Public Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" _
(ByVal sAgent As String, ByVal lAccessType As Long, ByVal sProxyName As String, _
ByVal sProxyBypass As String, ByVal lFlags As Long) As Long

Public Declare Function InternetConnect Lib "wininet.dll" Alias "InternetConnectA" _
(ByVal hInternetSession As Long, ByVal sServerName As String, ByVal nServerPort As Integer, _
ByVal sUsername As String, ByVal sPassword As String, ByVal lService As Long, _
ByVal lFlags As Long, ByVal lContext As Long) As Long

Public Declare Function FTPGetFile Lib "wininet.dll" Alias "FtpGetFileA" _
(ByVal hFtpSession As Long, ByVal lpszRemoteFile As String, _
      ByVal lpszNewFile As String, ByVal fFailIfExists As Boolean, ByVal dwFlagsAndAttributes As Long, _
      ByVal dwFlags As Long, ByVal dwContext As Long) As Boolean

Declare Function InternetGetLastResponseInfo Lib "wininet.dll" _
      Alias "InternetGetLastResponseInfoA" _
       (ByRef lpdwError As Long, _
       ByVal lpszErrorBuffer As String, _
       ByRef lpdwErrorBufferLength As Long) As Boolean
       
Declare Function FtpRemoveDirectory Lib "wininet.dll" Alias "FtpRemoveDirectoryA" (ByVal hFtpSession As Long, _
ByVal lpszDirectory As String) As Boolean
       
Private Declare Function FtpFindFirstFile Lib "wininet.dll" Alias "FtpFindFirstFileA" _
(ByVal hFtpSession As Long, ByVal lpszSearchFile As String, lpFindFileData As WIN32_FIND_DATA, ByVal dwFlags As Long, ByVal dwContent As Long) As Long

Private Declare Function InternetFindNextFile Lib "wininet.dll" Alias "InternetFindNextFileA" (ByVal hFind As Long, lpvFindData As WIN32_FIND_DATA) As Long

Function for getting the list..

Code:
Function FTPList(ByVal HostName As String, ByVal Username As String, ByVal Password As String, ByVal sDir As String) As String()
    
    On Error GoTo Err_Function
    
    Dim sOrgPAth As String
    Dim pData As WIN32_FIND_DATA
    Dim hFind As Long, lRet As Long
    Dim hConnection, hOpen, hFile  As Long
    Dim sFiles() As String

    sPath = String(MAX_PATH, 0)
    
    ' Open Internet Connecion
    hOpen = InternetOpen("FTP", 1, "", vbNullString, 0)
    
    ' Connect to FTP
    hConnection = InternetConnect(hOpen, HostName, INTERNET_DEFAULT_FTP_PORT, Username, Password, INTERNET_SERVICE_FTP, IIf(PassiveConnection, INTERNET_FLAG_PASSIVE, 0), 0)

    ' Change Directory
    Call FtpSetCurrentDirectory(hConnection, sDir)

    ' get list of directory
    Call FtpGetCurrentDirectory(hConnection, sPath, Len(sPath))
    
    pData.cFileName = String(MAX_PATH, 0)
    
    'find the first file
    hFind = FtpFindFirstFile(hConnection, "*.*", pData, 0, 0)
    
    'if there are files
    If hFind <> 0 Then
    
        'set first file
        ReDim Preserve sFiles(0)
        sFiles(UBound(sFiles)) = left(pData.cFileName, InStr(1, pData.cFileName, String(1, 0), vbBinaryCompare) - 1)
        Do
            'create a buffer
            pData.cFileName = String(MAX_PATH, 0)
            'find the next file
            lRet = InternetFindNextFile(hFind, pData)
            'if there's no next file, exit do
            If lRet = 0 Then Exit Do
            ReDim Preserve sFiles(UBound(sFiles) + 1)
            'add additional files
            sFiles(UBound(sFiles)) = left(pData.cFileName, InStr(1, pData.cFileName, String(1, 0), vbBinaryCompare) - 1)
        Loop
        
    End If

Exit_Function:

' Close Internet Connection
Call InternetCloseHandle(hOpen)
Call InternetCloseHandle(hConnection)
FTPList = sFiles
Exit Function

Err_Function:
    MsgBox "Error in FTPList : " & err.Description
GoTo Exit_Function
    
End Function

Then the actual code for obtaining the list of files...
Code:
    Dim myFiles() As String   
    
    ' get list of files
    myFiles = FTPList("127.0.0.1 - FTP Server IP", "MyUserID", "MyPWD", "/my/folder/path/")

You get back an array of the files / or folder names relative to the path passed in.

Hope it helps.

"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"

Free Dance Music Downloads
 
many thanks

If I enter Debug.Print myFiles I get a compile error: type mismatch
Any reason why?

Thanks
 
How do I fill a list box with the files names?

How do I download a file?

Many thanks
 
Alterbnatively the somewhat shorter:

Code:
[blue]Option Explicit

[green]' Requires references to Microsoft Shell Controls and Automation[/green]
Public Sub Example()
    Dim myFolderItem As FolderItem
    Dim localfolder As Folder
    Dim myShell As New Shell
    
    For Each myFolderItem In ftpList("ftp.microsoft.com/Products/Windows/Windows95/CDRomExtras/OtherUtilities/") [green]'Each item could be a folder or a file[/green]
        Debug.Print myFolderItem.Name, myFolderItem.IsFolder [green]' just for fun to illustrate stuff we can do[/green]
        If myFolderItem.Name = "olddos.exe" Then 
            Set localfolder = myShell.Namespace("F:\temp") [green]' or wherever your local folder is[/green]
            localfolder.CopyHere myFolderItem [green]' copy the required item[/green]
        End If
    Next
End Sub


[green]' Returns a FolderItems collection from the FTP server[/green]
Private Function ftpList(strFTPlocation As String, Optional strUser As String, Optional strPassword As String) As FolderItems
    Dim myShell As Shell
    Dim strConnect As String
    
    Set myShell = New Shell
    If strUser <> "" Then strConnect = strUser & ":" & strPassword & "@"
    Set ftpList = myShell.Namespace("FTP://" & strConnect & strFTPlocation).Items '("ftp://user:password@ftp.site.com")

End Function[/blue]
 
1DMF

I have all the code working. Thank you.

I have a last question (I think)
How do I check that an Internet connection actually exists?
I disconnected my computer from the internet and the code just runs fine!
 
There must be something in the wininet.dll API to check....

Having looked at
It says if the internet connection fails
Code:
hOpen = InternetOpen("FTP", 1, "", vbNullString, 0)

Then NULL should be returned by the InternetOpen function.

You probably need to add a check to see if hOpen is NULL and if so do some error handling.

Regards,
1DMF



"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"

Free Dance Music Downloads
 
you could actually go to what appears to be the original source of much of that FTP code,
Not sure how you work that out Mike, i've been using code like that since 2006/7 -> faq705-5904

And as 'less than dot' is copyrighted .. ©2008 - 2013 LessThanDot, LLC , and my FAQ out dates it by at least a year (and I was using the code prior to that), they probably found either my FAQ or the same place I originally got the code from ;-)

There is of course one other possibility... the code was on a different website and then rebranded in 2008 as LessThanDot , who knows. Is it actually you Mike?

But either way, claiming I got the code from there is clearly completely false as prooved by their website date VS my original FAQ date ;-)

"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"

Free Dance Music Downloads
 
Sorry, wasn't saying you got your code from there, just that some of it looked like it was derived from there and since they happened to include the error handling it was worth linking to. Of course it actually rather looks as if they ripped you off. Completely.
 
Hey Mike, like I said , I'd cribbed it from various sources on the net myself many years ago and as I give it away free as open source, it's all good :)

I too have a showerror routine
Code:
Sub ShowError()
   Dim lErr As Long, sErr As String, lenBuf As Long
   'get the required buffer size
   InternetGetLastResponseInfo lErr, sErr, lenBuf
   'create a buffer
   sErr = String(lenBuf, 0)
   'retrieve the last response info
   InternetGetLastResponseInfo lErr, sErr, lenBuf
   'show the last response info
   MsgBox "Last Server Response : " + sErr, vbOKOnly + vbCritical
End Sub

which is rather suspicously the same don't you think?

I'm suprised they didin't rip my code that includes a progress meter using the in-built syscmd meter :)

Code:
Function FTPFile(ByVal HostName As String, _
    ByVal Username As String, _
    ByVal Password As String, _
    ByVal LocalFileName As String, _
    ByVal RemoteFileName As String, _
    ByVal sDir As String, _
    ByVal sMode As String) As Boolean
    
    On Error GoTo Err_Function
        
' Declare variables
Dim hConnection, hOpen, hFile  As Long ' Used For Handles
Dim iSize As Long ' Size of file for upload
Dim Retval As Variant ' Used for progress meter
Dim iWritten As Long ' Used by InternetWriteFile to report bytes uploaded
Dim iLoop As Long ' Loop for uploading chuncks
Dim iFile As Integer ' Used for Local file handle
Dim FileData(BUFFER_SIZE - 1) As Byte ' buffer array of BUFFER_SIZE (100) elements 0 to 99

' Open Internet Connecion
hOpen = InternetOpen("FTP", 1, "", vbNullString, 0)

' Connect to FTP
hConnection = InternetConnect(hOpen, HostName, INTERNET_DEFAULT_FTP_PORT, Username, Password, INTERNET_SERVICE_FTP, IIf(PassiveConnection, INTERNET_FLAG_PASSIVE, 0), 0)

' Change Directory
Call FtpSetCurrentDirectory(hConnection, sDir)

' Open Remote File
hFile = FtpOpenFile(hConnection, RemoteFileName, GENERIC_WRITE, IIf(sMode = "Binary", FTP_TRANSFER_TYPE_BINARY, FTP_TRANSFER_TYPE_ASCII), 0)

' Check for successfull file handle
If hFile = 0 Then
    MsgBox "Internet - Failed!"
    ShowError
    FTPFile = False
    GoTo Exit_Function
End If

' Set Upload Flag to True
FTPFile = True

' Get next file handle number
iFile = FreeFile

' Open local file
Open LocalFileName For Binary Access Read As iFile

' Set file size
iSize = LOF(iFile)

' Iinitialise progress meter
Retval = SysCmd(acSysCmdInitMeter, "Uploading File (" & RemoteFileName & ")", iSize / 1000)

' Loop file size
For iLoop = 1 To iSize \ BUFFER_SIZE
        
    ' Update progress meter
    Retval = SysCmd(acSysCmdUpdateMeter, (BUFFER_SIZE * iLoop) / 1000)
        
    'Get file data
    Get iFile, , FileData
      
    ' Write chunk to FTP checking for success
    If InternetWriteFile(hFile, FileData(0), BUFFER_SIZE, iWritten) = 0 Then
        MsgBox "Upload - Failed!"
        ShowError
        FTPFile = False
       GoTo Exit_Function
    Else
        ' Check buffer was written
        If iWritten <> BUFFER_SIZE Then
            MsgBox "Upload - Failed!"
            ShowError
            FTPFile = False
            GoTo Exit_Function
        End If
    End If
    
Next iLoop

' Handle remainder using MOD

    ' Update progress meter
    Retval = SysCmd(acSysCmdUpdateMeter, iSize / 1000)

    ' Get file data
    Get iFile, , FileData
    
    ' Write remainder to FTP checking for success
    If InternetWriteFile(hFile, FileData(0), iSize Mod BUFFER_SIZE, iWritten) = 0 Then
        MsgBox "Upload - Failed!"
        ShowError
        FTPFile = False
        GoTo Exit_Function
    Else
        ' Check buffer was written
        If iWritten <> iSize Mod BUFFER_SIZE Then
            MsgBox "Upload - Failed!"
            ShowError
            FTPFile = False
            GoTo Exit_Function
        End If
    End If
               
Exit_Function:

' remove progress meter
Retval = SysCmd(acSysCmdRemoveMeter)

'close remote file
Call InternetCloseHandle(hFile)

'close local file
Close iFile

' Close Internet Connection
Call InternetCloseHandle(hOpen)
Call InternetCloseHandle(hConnection)

Exit Function

Err_Function:
MsgBox "Error in FTPFile : " & err.Description
GoTo Exit_Function

End Function

Which you'll see does make use of the error handling for failed internet / upload, again which is all in my original FAQ ;-)





"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"

Free Dance Music Downloads
 
The progress meter is great
in the code below, where would I call FTPFile to show the progress meter?
Code:
Function PutFileUsingFTP(ByVal HostName As String, ByVal UserName As String, ByVal Password As String, ByVal FileToPut As String, ByVal WhereToPut As String) As String()
    
On Error GoTo Err_Function

Dim sOrgPAth As String
Dim pData As WIN32_FIND_DATA
Dim hFind As Long, lRet As Long
Dim hConnection, hOpen, hFile  As Long

' Open Internet Connecion
DisplayMessage "Connecting to domain...."
hOpen = InternetOpen("FTP", 1, "", vbNullString, 0)

' Connect to FTP
hConnection = InternetConnect(hOpen, HostName, INTERNET_DEFAULT_FTP_PORT, UserName, Password, INTERNET_SERVICE_FTP, IIf(PassiveConnection, INTERNET_FLAG_PASSIVE, 0), 0)

'get file
DisplayMessage "Transferring file...."
Call FtpPutFile(hConnection, FileToPut, WhereToPut, 0, 0)
DisplayMessage "File Transferred file...."
Exit_Function:

' Close Internet Connection
DisplayMessage "Closing connection to domain...."
Call InternetCloseHandle(hOpen)
Call InternetCloseHandle(hConnection)

Exit Function

Err_Function:
    MsgBox "Error in GetFile : " & Err.Description
GoTo Exit_Function
    
End Function
 
You have to use chunks to either upload or download the file using the constant 'BUFFER_SIZE' and keep updating the progress meter after having intialised it with the total file size, which is basically this section..

Code:
' Get next file handle number
iFile = FreeFile

' Open local file
Open LocalFileName For Binary Access Read As iFile

' Set file size
iSize = LOF(iFile)

' Initialise progress meter
Retval = SysCmd(acSysCmdInitMeter, "Uploading File (" & RemoteFileName & ")", iSize / 1000)

' Loop file size
For iLoop = 1 To iSize \ BUFFER_SIZE
        
    ' Update progress meter
    Retval = SysCmd(acSysCmdUpdateMeter, (BUFFER_SIZE * iLoop) / 1000)
        
    'Get file data
    Get iFile, , FileData
      
    ' Write chunk to FTP checking for success
    If InternetWriteFile(hFile, FileData(0), BUFFER_SIZE, iWritten) = 0 Then
        MsgBox "Upload - Failed!"
        ShowError
        FTPFile = False
       GoTo Exit_Function
    Else
        ' Check buffer was written
        If iWritten <> BUFFER_SIZE Then
            MsgBox "Upload - Failed!"
            ShowError
            FTPFile = False
            GoTo Exit_Function
        End If
    End If
    
Next iLoop

' Handle remainder using MOD

    ' Update progress meter
    Retval = SysCmd(acSysCmdUpdateMeter, iSize / 1000)

    ' Get file data
    Get iFile, , FileData
    
    ' Write remainder to FTP checking for success
    If InternetWriteFile(hFile, FileData(0), iSize Mod BUFFER_SIZE, iWritten) = 0 Then
        MsgBox "Upload - Failed!"
        ShowError
        FTPFile = False
        GoTo Exit_Function
    Else
        ' Check buffer was written
        If iWritten <> iSize Mod BUFFER_SIZE Then
            MsgBox "Upload - Failed!"
            ShowError
            FTPFile = False
            GoTo Exit_Function
        End If
    End If
               
Exit_Function:

' remove progress meter
Retval = SysCmd(acSysCmdRemoveMeter)

If you check out the FAQ posted above it has both upload and download functions in it.

OR instead of trying to use the syscmd progress meter within your exisitng code if you can't work out the chunks part, you could simply just call FTPFile in your code passing in the required arguments.

Code:
' Upload file
If FTPFile("ftp.domain.com", "myUserName", "myPassword", "Full path and Filename of local file","Target Filename without path", "Directory on FTP server", "Upload Mode - Binary or ASCII") Then
    MsgBox "Upload - Complete!"
End If

It's very old code and could do with refactoring (now I know better), but it has worked for 6+ years in a production environment and served us well.

Hope it helps.

"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"

Free Dance Music Downloads
 
Of course, if you use my code, you get a progress meter for free... ;-)
 
No, no, not the code I linked. That's not mine at all. That's a dodgy copy of 1DMF's original code. I mean the much, much shorter code I posted in this thread 8 Feb 13 9:57. Perhaps you missed it ...
 
No, no, not the code I linked. That's not mine at all. That's a dodgy copy of 1DMF's original code. I mean the much, much shorter code I posted in this thread 8 Feb 13 9:57. Perhaps you missed it ...
I did not miss your post!
I cannot access the attachments.
The code appears incomplete and does not have a progress bar

Or am I missing something?
 
There are no attachments. The bits that appear to be attachments are a forum bug, and can be ignored. The code in the code box is complete (the blue text, basically). Nothing is missing. As I said, you get the progress bar for free in my code; you don't have to write code to show one, it is built-in to the CopyHere method
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top