• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 866
  • Last Modified:

Winsock - FTP best practice

Hi there,

I'm in the middle of writing some code to allow a user to FTP to a web server

The server uses Passive FTP and so I have two winsock controls, one to handle the commands and one to handle the
data transfer.

The code to log on to the server works fine and I have even worked out how to deal with parsing the IP address in
after I have sent the FTP command PASV.

The senddata commands and the server responses are all handled with a case statement unlike the code below.

I don't want any links to download other FTP programs as I am looking to write this project as a bit of a learning curve.
I have a number of questions hence the 500 points.

Public Sub SendData()
FileName = "c:\logo.png" - Hardcoded for the time being
txt_FileSize = FileLen(FileName)
Dim str() As Byte
Open FileName For Binary As #1
ReDim str(FileLen(FileName))
Get #1, , str
Close #1
Winsock1.SendData "STOR logo.png" & vbCrLf - Hardcoded for the time being
Winsock2.SendData str
End Sub

1) What is the best way to upload a file - The code above works but I have read that you should break the upload down into chunks.  How do I do this and why should I ?
2) When the code above starts uploading I get the server response 125 Data Connection already Open back from Winsock1, which is used to send the commands but nothing to tell me when the transfer is complete i.e. FTP Code 226 - Why ?
3) I get no server response on winsock2 in terms of FTP response, but I can track the number of bytes sent etc - Why ?


Public Sub DownloadData()
FileName = "c:\logo.png"
Open FileName For Binary Access Write As #1
Winsock1.SendData "RETR logo.png" & vbCrLf
Close #1
End Sub

Private Sub Winsock2_DataArrival(ByVal bytesTotal As Long)
Dim str() As Byte
Call Winsock2.GetData(str)
Put #1, , str
End Sub

4) The code above works to download a file - This really needs to be written properly - Any Suggestions ?? - I do get 226 Transfer Complete when it has finished.

Thanks in Advance

Paul
0
PaulEll
Asked:
PaulEll
  • 10
  • 9
  • 8
1 Solution
 
nffvrxqgrcfqvvcCommented:
If you are using FTP you should(or can) use wininet.dll API which is more reliable since it is used for the exact purpose of uploading / downloading files from an FTP(it is not an application but it is the proper way to access FTP for the task) it also allows you to create folders,remove folders,rename folders etc..I'm sure you can still do this with winsock but I prefer and recommend the API instead.  However if your just trying to learn winsock then it should be fine.

Why do you have 2 winsock controls? If you are making 2 connections to the same FTP I believe that would make your FTP slower and most of them only allow 1 connection at a time, I can't see your connection code , but are your closing the connection after the transfer is complete?

I can help you do this with winsock, but if you want to do it properly just ask me for my FTP API Sub and I will post it.
0
 
nffvrxqgrcfqvvcCommented:
This should help you with determining all the FTP commands you will need for  use with winsock

http://www.vbip.com/winsock/winsock_ftp_ref_01.asp

http://www.vbip.com/winsock/winsock_ftp_01.asp
0
 
nffvrxqgrcfqvvcCommented:
So in your code winsock1 sends the data and winsock2 establishes the connection I suppose
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
PaulEllAuthor Commented:
Hi there,

Because the web server that we are using works only in passive mode you need two winsock controls.
One winsock has to be used for sending commands  CWD \Folder\SubFolder
The other winsock control is for sending / getting the data.

I'm not sure what the advantages of passive ftp are but I know I cannot get any commercial FTP program to work
on this server unless I use passive FTP.

I don't mind using API's but as a newbie I find them a lot harder to follow then simple code that just interacts with
a single control.

Regards

Paul
0
 
PaulEllAuthor Commented:
HI there,

No it is the otherway around.

winsock1 is used to send the raw FTP commands i.e. Winsock1.SendData "RETR logo.png" & vbCrLf

The data returned is sent back via winsock2 i.e. Call Winsock2.GetData(str)

When you log on to a passive ftp server your are sent a server reply of 227 with an IP address plus two extra numbers
in order that you can connect to send / recieve data
IP xxx,xxx,xxx,xxx,12,202

The fifth figure is multiplied by 256 and then added to the sixth figure to give you your port to connect to

Winsock2.remotehost = "xxx,xxx,xxx,xxx"
winsock2.port = (12*256) + 202

Thats why I was sticking to standard winsock as API's with passive connections just seem to be really complicated when
you are at the bottom of a steep learning curve.

Regards

Paul
0
 
PaulEllAuthor Commented:
Hi Again,

The sites you gave are great I have already used them work out how to handle the FTP command PASV.

What I am really interested in is how to break the file up into binary chunks - what sizes to use and how best
to measure / monitor that his has been accomplished for both uploading and downloading.

Regards

Paul
0
 
nffvrxqgrcfqvvcCommented:
Paul, The code I am going to post is very simple to use. I will guide you on how to implement it into your application in 3 easy steps.  Give this a try as it is set up for passive mode.

'Start  a New Standard .EXE project.

'Step 1.
Add the following code or copy/paste the below code to Form_Declerations
'-------------------------------------------

Option Explicit
'FTP declares start
Private Const FTP_TRANSFER_TYPE_UNKNOWN = &H0
Private Const FTP_TRANSFER_TYPE_ASCII = &H1
Private Const FTP_TRANSFER_TYPE_BINARY = &H2
Private Const INTERNET_DEFAULT_FTP_PORT = 21
Private Const INTERNET_SERVICE_FTP = 1
Private Const INTERNET_FLAG_PASSIVE = &H8000000
Private Const INTERNET_OPEN_TYPE_PRECONFIG = 0
Private Const INTERNET_OPEN_TYPE_DIRECT = 1
Private Const INTERNET_OPEN_TYPE_PROXY = 3
Private Const INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY = 4
Private Const MAX_PATH = 260
Private Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type
Private 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

Private Declare Function InternetCloseHandle Lib "wininet" (ByRef hInet As Long) As Long
Private 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
Private 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
Private Declare Function FtpSetCurrentDirectory Lib "wininet.dll" Alias "FtpSetCurrentDirectoryA" (ByVal hFtpSession As Long, ByVal lpszDirectory As String) As Boolean
Private Declare Function FtpGetCurrentDirectory Lib "wininet.dll" Alias "FtpGetCurrentDirectoryA" (ByVal hFtpSession As Long, ByVal lpszCurrentDirectory As String, lpdwCurrentDirectory As Long) As Long
Private Declare Function FtpCreateDirectory Lib "wininet.dll" Alias "FtpCreateDirectoryA" (ByVal hFtpSession As Long, ByVal lpszDirectory As String) As Boolean
Private Declare Function FtpRemoveDirectory Lib "wininet.dll" Alias "FtpRemoveDirectoryA" (ByVal hFtpSession As Long, ByVal lpszDirectory As String) As Boolean
Private Declare Function FtpDeleteFile Lib "wininet.dll" Alias "FtpDeleteFileA" (ByVal hFtpSession As Long, ByVal lpszFileName As String) As Boolean
Private Declare Function FtpRenameFile Lib "wininet.dll" Alias "FtpRenameFileA" (ByVal hFtpSession As Long, ByVal lpszExisting As String, ByVal lpszNew As String) As Boolean
Private Declare Function FtpGetFile Lib "wininet.dll" Alias "FtpGetFileA" (ByVal hConnect As Long, ByVal lpszRemoteFile As String, ByVal lpszNewFile As String, ByVal fFailIfExists As Long, ByVal dwFlagsAndAttributes As Long, ByVal dwFlags As Long, ByRef dwContext As Long) As Boolean
Private Declare Function FtpPutFile Lib "wininet.dll" Alias "FtpPutFileA" (ByVal hConnect As Long, ByVal lpszLocalFile As String, ByVal lpszNewRemoteFile As String, ByVal dwFlags As Long, ByVal dwContext As Long) As Boolean
Private Declare Function InternetGetLastResponseInfo Lib "wininet.dll" Alias "InternetGetLastResponseInfoA" (lpdwError As Long, ByVal lpszBuffer As String, lpdwBufferLength As Long) 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
Private Const PassiveConnection As Boolean = True
'Ftp Declares end
'Additonal
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)


Sub Upload_WANIP(FS As String, UN As String, P As String, F As String, FilePath As String, UploadedFilename As String)
'FS=FTP SERVER
'UN=USERNAME
'P=PASSWORD
'F=FOLDERNAME TO CREATE ON FTP
'FilePath=Location of file on your pc to upload
'UploadedFilename=The filename of the uploaded file

DoEvents
Dim hConnection As Long, hOpen As Long, sOrgPath  As String
    hOpen = InternetOpen("FTP", INTERNET_OPEN_TYPE_PRECONFIG, vbNullString, vbNullString, 0)
    Sleep 500
    DoEvents
    hConnection = InternetConnect(hOpen, FS, INTERNET_DEFAULT_FTP_PORT, UN, P, INTERNET_SERVICE_FTP, IIf(PassiveConnection, INTERNET_FLAG_PASSIVE, 0), 0)
    Sleep 500
    DoEvents
    sOrgPath = String(MAX_PATH, 0)
    FtpGetCurrentDirectory hConnection, sOrgPath, Len(sOrgPath)
    Sleep 500
    DoEvents
    FtpCreateDirectory hConnection, F
   Sleep 500
   DoEvents
    FtpSetCurrentDirectory hConnection, F
  Sleep 500
   DoEvents
    FtpPutFile hConnection, FilePath, UploadedFilename, FTP_TRANSFER_TYPE_ASCII, 0
   Sleep 500
   DoEvents
    FtpSetCurrentDirectory hConnection, sOrgPath
     Sleep 500
   DoEvents
    InternetCloseHandle hConnection
     Sleep 500
   DoEvents
    InternetCloseHandle hOpen
 Sleep 500
   DoEvents
End Sub

'------------END OF Form_Declerations code------------




'Step 2.
'Create 1 command button on the form
'Add the following Call statement to a Command1 button
'------------------------------------------------------------------
'Step 3
'Then add your following FTP settings below::

Call Upload_WANIP("ftp.server.com", "username", "password", "WANIP", "c:\wanip.txt", "wanip.txt")

Regards,
EGL
0
 
nffvrxqgrcfqvvcCommented:
Sending data in chuncks means that you send a specific amount of bytes in each cycle or loop...You loop through the file and get say 1024 bytes until you reach the end of the file EOF, if EOF = 0 then the end of the file has been reached.
0
 
nffvrxqgrcfqvvcCommented:
'The above code uploads a file this will download a file, I have not tested this but it should work.

Sub DownloadFile(FS As String, UN As String, P As String, F As String, DownLfile As String, SaveDLedFile As String)
'F = directory on FTP where file is located.

DoEvents
Dim hConnection As Long, hOpen As Long, sOrgPath  As String
    hOpen = InternetOpen("FTP", INTERNET_OPEN_TYPE_PRECONFIG, vbNullString, vbNullString, 0)
    Sleep 500
    DoEvents
    hConnection = InternetConnect(hOpen, FS, INTERNET_DEFAULT_FTP_PORT, UN, P, INTERNET_SERVICE_FTP, IIf(PassiveConnection, INTERNET_FLAG_PASSIVE, 0), 0)
    Sleep 500
    DoEvents
    sOrgPath = String(MAX_PATH, 0)
    FtpGetCurrentDirectory hConnection, sOrgPath, Len(sOrgPath)
    Sleep 500
    DoEvents
   Sleep 500
   DoEvents
   'Set the directory to the file location
    FtpSetCurrentDirectory hConnection, F
  Sleep 500
   DoEvents
   'get file
    FtpGetFile hConnection, DownLfile, SaveDLedFile, False, 0, FTP_TRANSFER_TYPE_UNKNOWN, 0

   Sleep 500
   DoEvents
    FtpSetCurrentDirectory hConnection, sOrgPath
     Sleep 500
   DoEvents
    InternetCloseHandle hConnection
     Sleep 500
   DoEvents
    InternetCloseHandle hOpen
 Sleep 500
   DoEvents
End Sub



Call DownloadFile("ftp.server.com", "Username", "password", "bin", "myfile.exe", "c:\myfile.exe")
0
 
nffvrxqgrcfqvvcCommented:
If you are downloading /uploading binary files an not text files you can set the transfer types like this..

FTP_TRANSFER_TYPE_UNKNOWN = &H0
FTP_TRANSFER_TYPE_ASCII = &H1
FTP_TRANSFER_TYPE_BINARY = &H2

If your uploading a binary file then change the line in the sub to use binary. ie:

'For text files
 FtpPutFile hConnection, FilePath, UploadedFilename, FTP_TRANSFER_TYPE_ASCII, 0

'For binary files
 FtpPutFile hConnection, FilePath, UploadedFilename, FTP_TRANSFER_TYPE_BINARY, 0
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Hi PaulEll and egl1044,

I think both of you have a misconception of how FTP actually works.  It doesn't matter if you use the Winsock control, the WinInet APIs or some other third party communication control...when you transfer files via FTP you will use TWO connections.  This is just the way the FTP protocol was designed.  When using the WinInet APIs the second connection is simply transparent to you.  FTP always has one control connection for commands and responses, and one data connection for directory listings and file transfers.

The type of transfer, passive or active, does NOT determine whether or not you use two connections.  As I stated above, you always use two connections.  The difference between the two modes is how the data connection is initiated.  Here is an overview of the two:

(1) Passive Transfer - The FTP SERVER will LISTEN on an IP/Port that it chooses.  The IP/Port will be given to the client as part of the 227 response.  In Passive mode, the CLIENT will initiate the data connection with the SERVER.

(2) Active Transfer - The FTP CLIENT will LISTEN on an IP/Port that it chooses.  The IP/Port will be given to the server as part of the PORT command.  In Active mode, the SERVER will initiate the data connection with the CLIENT.

It doesn't matter whether you are uploading or downloading, they both work the same.  If the client is behind a firewall then Passive transfers may be the only way to go since an active transfer may be blocked.  The server, which is outside the firewall, must connect to the client which is behind the firewall.  This may be seen as an unsolicited attempt to connect to a machine behind the firewall.  Passive transfers alleviate this problem because the client machine initiates the data connection.  A connection going from inside the firewall to outside the firewall is normally allowed without scrutiny.

I hope this clears some things up...

~IM
0
 
PaulEllAuthor Commented:
Cheers for that IM,

I now see why the ISP is using Passive.

From what I have been reading, although I have not seen an example that I can understand,  when you upload a file
you should break it up into binary chunks and upload these chuncks one at a time until you reach the EOF.

With my exisiting code I use:-

Winsock1.SendData "STOR logo.png"  - This will create the file in the CWD of the FTP server but it will have no file
                                                          content / size until the binary upload has been completed.
Winsock2.SendData str - This command send the binary information

All the code I have written and is detailed at the top of this page works, but I am sure that it is a pretty pathetic example of VB/FTP coding.

Where I am stuck:-
1) How to break the file into chuncks
2) What is the best size (1024) appears to be the most recommended
3) How to detect the end of the transfer - wait for an FTP server response or calculate the number bytes being
sent against the size of the original file being uploaded.
4) Do I need to upload in a different way for .txt files or do I stilll break these into chuncks - I am aware that the FTP server needs to be notified via the TYPE command of what you are doing.

I then want to do the same for the "RETR" command and if at all possible make an allowance for the upload / download to
resume in the event an internet connection drops.  I understand the "REST" command can deal with this.

Thanks Paul
0
 
PaulEllAuthor Commented:
Thanks for all that egl1044 and the time you have taken.

I'll have a bash at putting it all together and trying to get it to work.

I'm looking at all those declarations which seem to mimic the FTP Commands i.e MKDIR = FtpCreateDirectory
Trouble is, knowing it works and knowing how it works are two seperate things.

I'll have to print it all out and have a good read through with the odd reference book around.

Regards

Paul
0
 
PaulEllAuthor Commented:
Hi there,

I've found the code below after searching  for two days and nights.

Is this the correct way to upload to an FTP server.  I have noticed that the code seems to read the data in
chunks and then sends it all at once.

Should this process be broken down into a format like READ a Chunk >>> UPLOAD a Chunk

I'll have to change the frp commands "FILE" and "FILEEND" as these do not appear to be supported by the
FTP server that I will have to use.

Regards

Paul

Public Sub SendData(sFile As String, sSaveAs As String, tcpCtl As Winsock)
On Error GoTo ErrHandler
    Dim sSend As String, sBuf As String
    Dim ifreefile As Integer
    Dim lRead As Long, lLen As Long, lThisRead As Long, lLastRead As Long
   
    ifreefile = FreeFile
   
    ' Open file for binary access:
    Open sFile For Binary Access Read As #ifreefile
    lLen = LOF(ifreefile)
   
    ' Loop through the file, loading it up in chunks of 64k:
    Do While lRead < lLen
        lThisRead = 65536
        If lThisRead + lRead > lLen Then
            lThisRead = lLen - lRead
        End If
        If Not lThisRead = lLastRead Then
            sBuf = Space$(lThisRead)
        End If
        Get #ifreefile, , sBuf
        lRead = lRead + lThisRead
        sSend = sSend & sBuf
    Loop
    lTotal = lLen
    Close ifreefile
    bSendingFile = True
    '// Send the file notification
    tcpCtl.SendData "FILE" & sSaveAs
    DoEvents
    '// Send the file
    tcpCtl.SendData sSend
    DoEvents
    '// Finished
    tcpCtl.SendData "FILEEND"
    bSendingFile = False
    Exit Sub
ErrHandler:
    MsgBox "Err " & Err & " : " & Error
End Sub
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Let's get you unstuck shall we?

(1) How to break the file into chunks

    In your example, you declare a byte array and then you redimension it so that it is the same size as your file.  This causes the entire file to be read into your buffer.  To read it in chunks, simply redim the buffer to the size you want (whatever chunk size you decide on -- see #2).  Then when you read from the file, it will only read a chunk of the file that matches your buffer size.  You will need to check the file position before you do each read.  If the file size minus the file position is less than your chunk size, then you must redimension your byte array to handle that "leftover" piece of file.  You can use the Seek() function to get the current position in your file.

    As far as sending the file in chunks you should be using the SendComplete() event from your Winsock control.  Each time you send data with the Winsock control, you will get the SendComplete() event when the data has been received.  It is then that you should read the next chunk from your file and send again.  If you are NOT using the SendComplete() event and you are simply reading in chunks and sending them...then what was the point of reading in chunks in the first place!?.  You are essentially flooding the data stream and forcing your packets to be joined and/or fragmented along the way.

(2) What is the best size?

    There is great debate over this topic and the correct response is there isn't one perfect value that will cause your app to perform optimally under all conditions.  This is because as your packets travel across the internet they may be broken up or joined together along the way.  You have no control over this as it may or may not occur at each router/server that passes your packet(s) along.  So as a general rule of thumb, pick a size between 1024 and 4096 bytes.  I have used 4096 with good results myself.

(3) How to detect the end of the transfer ?

    "Do I wait for an FTP server response or calculate the number bytes being sent against the size of the original file being uploaded?"

    Both...and more!  The 226 response is normally sent to signal that the transfer is complete.  It should be noted however that the 226 response may be received while there is still data streaming across the data connection.  Sometimes the data connection closes before you get the 226 response.  You can't be sure which you will get first since this isn't really specified by the FTP protocol.  The 226 really only indicates that there were no apparent errors during transmission.  The closing of the data connection is what actually signals that the transfer is complete.

    Since you have no way of knowing if the data connection was closed because the entire file was transferred or if your connection was prematurely cut off, then you should ALWAYS check the size of your source file against the size of your target file when the data connection closes.  You can check the file size on the FTP server using the SIZE command.

(4) Do I need to upload in a different way for .txt files?

    The data connection and the FTP client/server have no idea what you are sending across it.  Both binary and text files can be sent in chunks or as one whole piece.

    The purpose the TYPE command is to tell the server if any automatic conversions should be done.  If binary mode is specified then the file will be sent as is, bit by bit, and you will get an exact copy.  If ascii mode is specifed then the server will attempt to convert end of line characters for you.  On Unix systems, and end of line is usually specified with just a line feed (Lf).  On windows systems, and end of line is specifed with a carriage return/line feed combination (CrLf).  So if you send a plain text file in ascii mode, then the server will convert the end of line markers for you so that it appears the same on both systems.  I hope you can see that sending a binary file in ascii mode will most likely corrupt the file.  

    So the Type mode has nothing to do with whether you send in chunks or not.  The two are completely independent of each other.

(5) How do I implement file transfer resumes?

    You've got the right idea with the REST command.

    If you are downloading then you check the local file size and compare it the remote file size returned by the SIZE command.  If the local file size is smaller than the remote size, then a resume is in order.  You would issue the REST command along with the local file size.  Then you open the local file and move the file pointer to the end of the file using the Seek() function.  This will cause the additional bytes received from the FTP server to be appended to your local file.

    Uploading is essentially the same thing.  If the remote file size is smaller than your local file size then a resume is in order.  You would issue a REST command using the value returned from the SIZE command.  This will cause data received to be appended to the remote file.  (An alternative would be to skip the REST command and use the APPE command instead of STOR.)  Then you would open your local file and move the file pointer using the Seek() function based on the remote file size.

    You have to be careful when implementing resumes however.  Just becuase the file sizes are different doesn't mean you have a partial upload/download.  It is always possible that the source and/or target file has been changed and/or overwritten with a different version of the file but with the same name.  It is up to you decide how to handle these situations.  Sometime is is possible to give unique names to files so you can differentiate versions based upon the name itself instead of the create/modification dates.  The process of getting the date/time of a file on an FTP server is beyond the scope of this post since not all FTP servers support this feature or even implement it in the same way.
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Regarding your last post PaulEll...

No.  That is not the "correct" way to send a file in chunks.  Please see the second half of my response in for #1 above.

In fact, the code you have posted there isn't for an FTP transfer at all.  It looks like a custom file transfer app that is utilizing two connections much like an FTP session does.

~IM
0
 
PaulEllAuthor Commented:
Thanks IM for your post.

I now fully understand the logic of using the winsock control and the processes involved in order to use the winsock
control properly

I am half way there now, all I have to do is write the VB code that can achieve this. I'll have a search for info on the Seek() function as I have not come across this before and see what I can muddle together.

Thanks for ALL the effort.
Regards
Paul
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
No problem PaulEll...

I have given you lots of general advice on how to work with the Winsock control.  If you need help with the specifics, don't hesitate to post questions regarding the implementation of these ideas.  I'm sure we can get your problems solved between all the experts here at EE.



egl1044,

I've never tried the WinInet API approach that you have presented.  It appears to work at a higher level...meaning you don't need to worry about managing the data connection and parsing the server responses.  But one thing I do not see is any kind of feedback for a progress bar during transfers.  (All of the FTP functions appear to be synchronous code blocking calls.)  Do you know how to integrate progress feedback into your code or make the calls asnychronous?

~IM

0
 
PaulEllAuthor Commented:
Hi IM,

I admit defeat.

I've been struggling with this now for a few days. - I've looked at previous questions and have spent many an hour googling.
If you can help me out with this last point I'll post another question and award you the points as we have moved on
a bit from my initial question.

1) When you open the file in binary format I assume you start at <address> 0

2) You use the get statement to load a specific number of bytes <4096> into a String Buffer

3)  How the hell do you get the loop / read process to access the next chunk, in this instance bytes 4097 - 8192

Public Sub ReadLocalData()
Dim iFreeFile As Integer
Dim LocalFile As String
Dim ChunkSize As Long
Dim SendBuffer As String
Dim LFileLen As Long

iFreeFile = FreeFile
LocalFile = "c:\test.mpg" ' This has a file size of 725,000 bytes
ChunkSize = 4096

Open LocalFile For Binary Access Read As #iFreeFile
LFileLen = LOF(iFreeFile)

' Get the first Chunk
Get #iFreeFile, ChunkSize, SendBuffer

' Call the routine to upload SendBuffer - Return When it is sent
 
' Code to move to the next Chunk

Close #ifreefile

Thanks

Paul
0
 
nffvrxqgrcfqvvcCommented:
Yes, Idle Mind there is API that reads the file size into a buffer.

Private Declare Function FtpGetFileSize Lib "wininet" _
(ByVal hConnect As Long, _
lpdwFileSizeHigh As Long) As Long

0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
egl1044,

So you can get the size of the file via the FtpGetFileSize() API but that doesn't help with a progress bar if the calls to transfer files via FtpGetFile() and FtpPutFile() are blocking calls.  You probably have to set up some kind of callback function to achieve transfer progress...

~IM
0
 
nffvrxqgrcfqvvcCommented:
Idle Mind refer here you will find all the answers http://www.expressnewsindia.com/c4/ex1/C953.html
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
PaulEll,

The second parameter to the Get function is the record position.  So in this line:

    Get #iFreeFile, ChunkSize, SendBuffer

You are actually asking for the bytes at position ChunkSize and placing them into SendBuffer.  You can omit the second parameter to make the next read occur at the current file pointer position:

    Get #iFreeFile, , SendBuffer

After each read, the file pointer will be moved by however big SendBuffer is...ready for the next chunk to be read.  You still need to be declaring your buffer as a byte array.  Here is some basic code that will read your file in "chunks":

Option Explicit

Private Sub Command1_Click()
    Dim iFreeFile As Integer
    Dim LocalFile As String
    Dim ChunkSize As Long
    Dim SendBuffer() As Byte
    Dim LFileLen As Long

    iFreeFile = FreeFile
    LocalFile = "c:\test.mpg"
    ChunkSize = 4096
    ReDim SendBuffer(ChunkSize - 1)

    Open LocalFile For Binary Access Read As #iFreeFile
    LFileLen = LOF(iFreeFile)
       
    While Loc(iFreeFile) < LFileLen
        ' adjust the chunk size if necessary for the end of the file
        If (LFileLen - Loc(iFreeFile)) < ChunkSize Then
            ReDim SendBuffer(LFileLen - Loc(iFreeFile) - 1)
        End If
       
        ' Get the NEXT Chunk
        Get #iFreeFile, , SendBuffer
       
        ' Call the routine to upload SendBuffer - Return When it is sent
    Wend
     
    Close #iFreeFile
End Sub
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
You can replace the byte array with a string...

Then instead of ReDim SendBuffer(XXX) you would use SendBuffer = Space(XXX).
0
 
PaulEllAuthor Commented:
Thanks IM for you help sorry to take so long to reply but I have been away.

I've awarded you the points as we have strayed some what from the original question
and all your efforst should be rewarded as your answers have been very thorough and extremely helpful.

I am still having trouble understanding this code and will upload a further question with the
same number of points later after I have fully commented it to display my total lack of knowledge

I am sure that I am being totally thick but I still cannot see how it can do the following:-

Say we have a file that is 200,000 bytes long.

To me the code appears to say: -

While our location in the file is less than its total length - Do Something - Ok so far !

If the FileLength minus Our current location is less than the chunksize then - Do something else - I'm still follwing it up until now.

This is the problem

ReDim SendBuffer(LFileLen - Loc(iFreeFile) - 1)

If our current location is 1 then this say load everything i.e. 200,00 minus 1 not a chunksize of 4096

If we use the space command

SendBuffer = space(4096) - Does this mean read the first 4096 bytes ?? And if so will this move the
read process on automatically.

I did try to re-write the code but got loads of errors, rather than smash the keyboard with frustration
I thought I'd ask another question.

Regards

Paul





0
 
PaulEllAuthor Commented:
Hi,

New question posted here, if you are not sick of me already.

http://www.experts-exchange.com/Programming/Programming_Languages/Visual_Basic/Q_21434021.html

Regards

Paul
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Initially, the chunk size was set at 4096 with this statement:

    ReDim SendBuffer(ChunkSize - 1)

Then the only time it gets changed is if the REMAINING portion of the file is less than our chunk size.  That is what the If condition is checking for:

        If (LFileLen - Loc(iFreeFile)) < ChunkSize Then
            ReDim SendBuffer(LFileLen - Loc(iFreeFile) - 1)
        End If

So we continue to read in chunks of 4096 until we reach the end of the file and get a "leftover" part that is smaller than our chunk size.  Then we redimension the buffer and read the last bit.  (Of course, it is always possible that our file size is a multiple of our chunk size and there is no remaining bit.)

Make sense?
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

  • 10
  • 9
  • 8
Tackle projects and never again get stuck behind a technical roadblock.
Join Now