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 strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Writing Chunks for FTP (internetwritefile) PLEASE HELP!

Status
Not open for further replies.

1DMF

Programmer
Jan 18, 2005
8,795
GB
this is driving me mad, I can't find a single example that explains how to use the internetwritefile function in the wininet.dll (well in simple plain english anyway).

i've found this
Code:
INTERNET_BUFFERS ibBuffer;
ZeroMemory (& ibBuffer, sizeof (INTERNET_BUFFERS));
ibBuffer.dwStructSize  = sizeof (INTERNET_BUFFERS);
ibBuffer.dwBufferTotal = nBufferSize;

if ( !HttpSendRequestEx (hRequest, & ibBuffer,
                         NULL, HSR_INITIATE, 0))
{
    // handle error
}

// now enter the buffer in chunks of 1024 bytes
DWORD         dwBytesSend;
int           nPortion = nBufferSize / 10;
const int     nMinToSend = __max (nPortion, g_HTTPChunkSize);
int           nChunkSize = nMinToSend;
int           nBufferPos = 0;
int           nPrevPercentage = 0;
char          *pBufferPtr = g_szGlobalBuffer;

while (nBufferPos < nBufferSize)
{
    if (! InternetWriteFile (hRequest, pBufferPtr, nChunkSize,
                             & dwBytesSend))
    {
        m_WSError.SetErrorDescr (L"InternetWriteFile", __FILE__,
                                 __LINE__, GetLastError ());
        HttpEndRequest (hRequest, NULL, 0, 0);
        goto Cleanup;
    }

    pBufferPtr += nChunkSize;
    nBufferPos += nChunkSize;
    if (nBufferSize - nBufferPos < nMinToSend)
        nChunkSize = nBufferSize - nBufferPos;
    else
        nChunkSize = nMinToSend;
}

but this is not VBA and doesn't make sense to me.

why is a buffer defined as a string when uploading a BINARY file, what bit sets the file size, what bit tells it the chunk size to upload, what bit uploads the chunk, what bit keeps track of what is already uploaded, what bit keeps track of the remainder when the file isn't divisible by the chunk size exactly , there will be some bytes left over won't there.

It's driving me mad, because I know it's actually quite simple to do if someone bothered to write proper code with comments, but not even MS has examples of how to upload a file using this API.

Someone PLEASE HELP!

 
well i've got as far as this but all I get is a zero byte file on the server, the InternetWriteFile just keeps returning 0 (False).

can anyone help why the InternetWriteFile command doesn't work.

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) As Boolean
        
' Declare variables
Dim hConnection, hOpen, hFile  As Long
Dim iSize As Long
Dim Retval As Variant
Dim iWritten As Long
Dim iLoop As Long
Dim iFile As Integer
Dim FileData As String

' Open Internet Connecion
hOpen = InternetOpen("HLP", 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)

' create file
hFile = FtpOpenFile(hConnection, RemoteFileName, GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY, 0)

' check for successful file handle
If hFile = 0 Then
    FTPFile = False
    GoTo Exit_Function
End If

'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)

'set string buffer to BUFFER_SIZE and fill with spaces
FileData = Space(BUFFER_SIZE)
    
' 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 Not InternetWriteFile(hFile, FileData, BUFFER_SIZE, iWritten) Then
        FTPFile = False
       GoTo Exit_Function
    End If

Next iLoop

' handle remainder using MOD

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

    'set string buffer to REMAINDER and fill with spaces (char 32)
    FileData = Space(iSize Mod BUFFER_SIZE)

    'get file data
    Get iFile, , FileData
    
    ' write remainder to FTP checking for success
    If Not InternetWriteFile(hFile, FileData, iSize Mod BUFFER_SIZE, iWritten) Then
        FTPFile = False
        GoTo Exit_Function
    End If
        
Exit_Function:

' remove progress meter
Retval = SysCmd(acSysCmdRemoveMeter)

'close local file
Close iFile

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

End Function
 
Couple of questions - why are you writing chunks? what are you trying to achieve with the function?

In the past I use FTPPutFile - to upload a marker file - ie a text file with a date in - this can then be downloaded - checked from another system - which then proceeds with additional downloads if the date is new.


If at first you don't succeed, try for the answer.
 
I'm trying to upload LARGE BINARY files that cannot be uploaded with FtpPutFile as stated by Microsoft and you should instead upload in chunks via InternetWriteFile.

It also means you can give a progress meter if uploading in chunks, this is how ALL FTP programs do it underneath the hood if they use the wininet.dll API.

I have FtpPutFile woking fine for our system, it just can't handle large files, and I can't understand why my code above doesn't work

Can you help ?
 
So it is failing to write the file to the server - well the chunk of the file.

The logic of your function appears valid and sound (as you say) which would suggest that something is not as it should be, by which I mean a variable. Is Getfile returning a valid result? What have you tried doing with the filedata?

Are the paths it is trying to upload, and upload to correct? IE what happens if you make the file smaller and PUT the file? What is the last error code returned by the connection? We get loads of 12011 and 12013 problems that are due to heavy server loads.

I havent had to use this method for a long time and no longer have the code that I used. And my memory is not as sharp as it should be when trying to remember code specifics.



If at first you don't succeed, try for the answer.
 
Ok, here is where i've got to.

1. connection is fine
2. traverse to correct directory is fine
3. ftpopenfile creates the file on server fine ready to fill with the chunks of data.
4. OPEN file and the GET seems to be fine and if i display the buffer 'FileData' i get the usual text representation of binary (those blocks and control codes)
it's just the internetwritefile command instantly returns 0.

and the same when I use the showerror for getlastresponse.

all I end up with is the correct file, in the correct directory on the correct server but of ZERO bytes file size. - even the progress bar works like a dream, just no bytes are transferred to the FTP server.

so close - yet so far!

 
More dumb questions - but this is far from a simple fix question is it?

When it fails what is BUFFER_SIZE? What is iWritten? What happens when you step through the code? It should fail with an error message ie GETLASTERROR - but what does it do? With the open file, can you - close it? get file info from it? download the file?

I am going to test my end.

What have you sent GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY as in your constant lists - I am too lazy to dig them out myself



If at first you don't succeed, try for the answer.
 
ok here are the constants and declarations
Code:
' used for FTP
Const FTP_TRANSFER_TYPE_UNKNOWN = &H0
Const FTP_TRANSFER_TYPE_ASCII = &H1
Const FTP_TRANSFER_TYPE_BINARY = &H2
Const INTERNET_DEFAULT_FTP_PORT = 21
Const INTERNET_SERVICE_FTP = 1
Const INTERNET_FLAG_PASSIVE = &H8000000
Const GENERIC_READ = &H80000000
Const GENERIC_WRITE = &H40000000
Const PassiveConnection As Boolean = True
Const BUFFER_SIZE As Long = 1024

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 FtpOpenFile Lib "wininet.dll" Alias _
"FtpOpenFileA" (ByVal hConnect As Long, ByVal lpszFileName As String, _
 ByVal fdwAccess As Long, ByVal dwFlags As Long, ByVal dwContext As Long) As Long

Private Declare Function InternetWriteFile Lib "wininet.dll" (ByVal hConnect As Long, ByVal lpBuffer As String, _
 ByVal dwNumberOfBytesToWrite As Long, ByVal lpdwNumberOfBytesWritten As Long) As Boolean

Private Declare Function InternetCloseHandle Lib "wininet.dll" (ByVal hInet As Long) As Integer

Private Declare Function FtpSetCurrentDirectory Lib "wininet.dll" Alias _
"FtpSetCurrentDirectoryA" (ByVal hFtpSession As Long, ByVal lpszDirectory As _
String) 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 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 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 FtpCommand _
Lib "wininet.dll" Alias "FtpCommandA" (ByVal hConnect As Long, _
ByVal fExpectResponse As Boolean, ByVal dwFlags As Long, ByVal lpszCommand As String, _
ByVal lContext As Long, ByVal phFtpCommand As Long) As Boolean

I have also uploaded (via normal FTP - lol) a cut down version with the code for you so hopefully that might help, but we use office 2003 so i hope it is compatible with your Access.


I look forward to your comments.

Regards,

1DMF
 
Just as I got the mail I got the code working - by which I mean not working IE getting the same behaviour as you.

I have been working for the last 9 hours without a break so the mind is not totally focussed

I will get back to you a bit later if I have any luck


If at first you don't succeed, try for the answer.
 
Hey - all work and no play, makes jack a dull boy, don't burn yourself out, well not till you fixed my problem at any road :)

no seriously I know how you feel I was up till 1am this morning working on it after having done an eight hour day in the office I took it home and did another 6 hours looking at every google link for internetwritefile I could find, even a microsoft link which had a box for you to send back comments so i did.....here is the email...
Code:
--------------------------------------------------------------------------------
From: SDKFDBK@Microsoft.com [mailto:SDKFDBK@Microsoft.com] 
Sent: 07 June 2005 08:00
To: sspl@stepnstomp.co.uk
Subject: CST194370410ID - RE:TITLE: FTP Sessions [WinINet]; RELEASE: March 2005; URL: wininet/ftp_sessions.xml

Hello Craig, 

Thank you for sending your comments to the Platform SDK feedback alias. 

I understand that you are having difficulties looking for an example on how to use InternetWriteFile. I apologize for the inconvenience this may have caused you. 

The issue has been redirected to the appropriate team for further review. If you need immediate developer support please review the support options on the 

Microsoft Product Support site at [URL unfurl="true"]http://support.microsoft.com/[/URL] 

Sincerely, 

Ampee 

The Platform SDK Team 

_ Live technology chats with Microsoft employees: [URL unfurl="true"]http://msdn.microsoft.com/chats/[/URL] 

_ Developer-focused newsgroups: [URL unfurl="true"]http://msdn.microsoft.com/newsgroups/[/URL] 

--- Original Message --- 

From: sspl@stepnstomp.co.uk 

To: sdkfdbk@css.one.microsoft.com 

Sent: Mon Jun 6 11:15:31 PST 2005 

Subject: TITLE: FTP Sessions [WinINet]; RELEASE: March 2005; URL: wininet/ftp_sessions.xml 

Why is there no example for using InternetWriteFile ? 

I've spent days trawlling the net and can't find a single example that works for InternetWriteFile using VBA 

Please Help 

Regards, 

Craig Chant

roughly translated, "It's nothing to do with me, so I've passed the buck, but if you'd like to get out your credit card then we'd be happy to help!

don't you just love Microsoft!
 
I have made progress of sorts.

If I upload a file to the ftp server manually containing data - then run the code - reopen the file - it is blank - so the process is opening a new file even when one exists.

I have found suggestions that InternetWriteFile always returns 0 but no definitive answer.

This is turning my brain inside out!!

If at first you don't succeed, try for the answer.
 
Tell me about it, i've frazzled mine trying to work this out.

As for the file existing it just overwrites it, that is what i wanted anyhow, I'm not interested if the file exits or not - i want it either created or overwitten.

hmmm I just don't know what else to do to make this work.

any other suggestions are very welcome.
 
OK - this is interesting - with the filehandle I try getting a filesize - and get back -1.

I am going try some stuff tomorrow with file handles that are set to read - see if I can read from a file AND also get file properties from the handle.

I even tried passing a fixed piece of text much smaller then the buffer and still nothing.

This is more perculiar then my dropped FTP connections when you I do a FindFirstFile

If at first you don't succeed, try for the answer.
 
They say these things are sent to try us, but I think Microsoft are here to defeat us!
 
I've managed to solve this problem. I shall post in a new thread with the answer for everone who wants it.
 
I tried the (0) thing and it didnt seem to work my end.

I ran out of time to look any further yesterday

If at first you don't succeed, try for the answer.
 
You need to change the declare for InternetWriteFile buffer defenition from string to be Byte var type.

set the BUFFER_SIZE constant to 100

Then in the FTPFile function you need to Dim the FileData buffer as a byte array to size of buffer minus 1 as arrays start @ zero like so

Code:
Dim FileData(BUFFER_SIZE - 1) as Byte

the loop and file size bits then all work when you divide and mod using BUFFER_SIZE

that should then do the trick



 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top