Link to home
Start Free TrialLog in
Avatar of baralong
baralong

asked on

Posting a file to an HTTP server

I have a CGI type web page that recieves an image file from a form that looks like:

<form enctype="multipart/form-data" action="http://server/accept.pl" method="post">
<input name="my_file" type="file" size="20">
</form>

The server accepts the file and saves it.

This works fine when posted from a web page but I need to be able to invoke it from VB. I've been trying to use the Internet Transfer Control with an Execute command but with no luck.

I don't have the option to transfer the file using FTP.

Thanks
Avatar of baralong
baralong

ASKER

Adjusted points to 500
Adjusted points to 1000
The easiest way is to use the WebBrowser control. Create a html file with your form and show it in the control. It is possible to submit your form with the Document property of this control.
Using the Navigate method of the webbrowser control you can send data...

To add this control to your program, right-click on the toolbox and select "Components..." then select "Microsoft Internet Controls" (SHDOCVW.DLL) from the Controls box.

The help file for this control is not installed on your system, but it is on the Visual Basic CD, in the directory:

  \TOOLS\UNSUPPRT\WEBBRWSR

Here is the help page for navigate:

NAVIGATE METHOD

Navigates to the resource identified by a Universal Resource Locator (URL), or to the file identified by a full path.

Syntax

object.Navigate url, flags, TargetFrameName, PostData, Headers

The GetProperty method syntax has these parts:

Part       Description
object      An object expression that evaluates to an object in the Applies To list.
Url      Required. A string expression that evaluates to the URL of the resource to display, or the full path of the file to display.
Flags      Optional. A constant or value that specifies whether to add the resource to the history list, whether to read from or write to the cache, and whether to display the resource in a new window. It can be a combination of the following values. See Settings below.
TargetFrameName      Optional. A string expression that evaluates to the name of a frame in which to display the resource.
PostData      Optional. Data to send to the server during the HTTP POST transaction. For example, the POST transaction is used to send data gathered by an HTML form. If this parameter does not specify any post data, the Navigate method issues an HTTP GET transaction. This parameter is ignored if URL is not an HTTP URL.
Headers      Optional. A value that specifies additional HTTP headers to send to the server. These headers are added to the default Internet Explorer headers. The headers can specify such things as the action required of the server, the type of data being passed to the server, or a status code. This parameter is ignored if URL is not an HTTP URL.
Settings

The settings for flags are:

Constant      Value      Description
navOpenInNewWindow      1      Open the resource or file in a new window.
navNoHistory      2      Do not add the resource or file to the history list. The new page replaces the current page in the list.
navNoReadFromCache      4      Do not read from the disk cache for this navigation.
navNoWriteToCache      8      Do not write the results of this navigation to the disk cache.



Cheers!
mcrider, your method is too hard - you have to encode the file and do all other stuff yourself while it can be done by the WebBrowser control... And your answer is very incomplete, please post such answers as comments.
vmv, I know when to post an answer... I suggest you find my name in the top 15 experts in this category.

As for having to URLencode the Post buffer being sent, Adding the following to a module and calling the URLencode function will accomplish this.


    Function ReplaceInString(Source As String, TheString As String, TheReplaceString As String) As String
        Dim iVal As Integer
        Dim Workspace As String
        Workspace = Source
        iVal = 1
        Do
            iVal = InStr(iVal, Workspace, TheString, 1)
            If iVal = 0 Then Exit Do
            Workspace = Left$(Workspace, iVal - 1) + TheReplaceString + Mid$(Workspace, iVal + Len(TheString))
            iVal = iVal + 1
        Loop
        ReplaceInString = Workspace
    End Function
    Function StripHTML(Source As String) As String
        Dim iVal As Long
        Dim Buf As String
        Dim lBuf As String
        Dim rBbuf As String
        iVal = InStr(1, Source, "<")
        If iVal = 0 Then
            StripHTML = Source
            Exit Function
        End If
        lBuf = Left$(Source, iVal - 1)
        iVal = InStr(iVal, Source, ">")
        rbuf = Mid$(Source, iVal + 1)
        Buf = lBuf + " " + rbuf
       
        Do
            iVal = InStr(1, Buf, "<")
            If iVal = 0 Then
                StripHTML = Buf
                Exit Do
            End If
            lBuf = Left$(Buf, iVal - 1)
            iVal = InStr(iVal, Buf, ">")
            rbuf = Mid$(Buf, iVal + 1)
            Buf = lBuf + " " + rbuf
        Loop
    End Function
    Function URLdecode(Source As String) As String
        Dim lBuf As String
        Dim iVal As Long
        lBuf = ReplaceInString(Source, "+", " ")
        iVal = 1
        Do
            iVal = InStr(iVal, lBuf, "%")
            If iVal = 0 Then Exit Do
            lBuf = Left$(lBuf, iVal - 1) _
                + Chr$(CLng("&H" + Mid$(lBuf, iVal + 1, 2))) _
                + Mid$(lBuf, iVal + 3)
            iVal = iVal + 1
        Loop
        URLdecode = lBuf
    End Function
    Function URLencode(Source As String) As String
        Dim iVal As Long
        Dim lBuf As String
        Dim lArray As Variant
        lBuf = Source
        For Each lArray In Array("%", "&", "+", "~", "`", "!", "@", "#", "$", "^", "*" _
            , "(", ")", "-", "_", "=", "{", "[", "}", "]", "|", "\", ":", ";", "'", "<" _
            , ">", "?", ",", ".", "/", Chr$(34))
            lBuf = ReplaceInString(lBuf, CStr(lArray), "%" + Right$("0" + Hex(Asc(lArray)), 2))
        Next
        lBuf = ReplaceInString(lBuf, " ", "+")
        iVal = 0
        Do
            iVal = iVal + 1
            If iVal > Len(lBuf) Then Exit Do
            lArray = Mid$(lBuf, iVal, 1)
            If Asc(lArray) <= 31 Or Asc(lArray) >= 127 Then
                lBuf = ReplaceInString(lBuf, CStr(lArray), "%" + Right$("0" + Hex(Asc(lArray)), 2))
            End If
        Loop
        URLencode = lBuf
    End Function
mcrider,
>I suggest you find my name in the top 15 experts in this category.

This just means that you was here for a long time posting comments as answers.

>As for having to URLencode the Post buffer

Why you want to write kilobytes of unneded (maybe buggy) code while all this stuff is already implemented in the WebBrowser? Why you don't post assembly source for this?
Thanks for the help so far.

mcrider, I don't see how any of the functions help me send a binary file. I realise I need to encode the file but how?

I haven't had a chance to try either approach out, and won't until Monday.
baralong,

Basically, you do this:

Dim PostData As String
Dim FileData As String

'Put your code to read the file into the string variable "FileData" here...

'Convert the string into a URL Encoded string...
PostData = URLencode(FileData)

'Send the encoded data to your server...
WebBrowser1.Navigate "http://server/accept.pl", , , PostData


Cheers!
mcrider, sorry but I can't get this to work. My CGI script can't doesn't recieve the file correctly

vmv, your method seems promising. I have to admit I'm quite a novice when it comes to VB so I'm probably making this more complex than it needs to be.

The problem I'm having with this is: I get the Document property but can't seem to get any lower, I thought it would be an "HTMLDocument" but it's not working well.

if the form detail is:

<form enctype="multipart/form-data" action="http://server/accept.pl" method="post" id="form1">
<input name="my_file" type="file" size="20" id="my_file">
<input type="submit" id="sub">
</form>

can you post code to set the value of "my_file" and then submit the form.

I have been trying with:

Dim HTMLDoc As HTMLDocument
Set HTMLDoc = WB.Document ' OK up to here

Dim Elem As HTMLFormElement
Set Elem = HTMLDoc.Forms("form1") ' here is where it breaks.

Thanks
baralong,

Post a copy of your perl CGI script...


Cheers!®©
Unfortunately I never tried to do something similar, so I don't know how to get this to work - that comment was just an idea of a way to work on. I suggest you to look for some example in the MSDN.
baralong,

If you dont have Service Pack 2 or 3 installed, the INET "post" will not work.  See the following microsoft KB articles:

FIX: Internet Transfer Control 5.0 "POST" Request Doesn't Work
http://support.microsoft.com/support/kb/articles/Q167/7/06.asp?LNG=ENG&SA=MSDN&FR=0 
 
INFO: Visual Studio 97 Service Packs - What, Where, and Why
http://support.microsoft.com/support/kb/articles/Q170/3/65.ASP 

INFO: Visual Basic 5.0 Fixes in Visual Studio 97 Service Pack 2
http://support.microsoft.com/support/kb/articles/Q171/5/54.asp?LNG=ENG&SA=MSDN&FR=0 
 

Cheers!®©

 
mcrider,

I'm at home and I don't have a copy of the script but the detail that saves the file, but it's basically something like:

use CGI;
$query= new CGI;
$fileup = $query->param('my_file');

# extract its name
$strFilename = $fileup;
$strFilename =~ s/.*\\([^\s\\]+)$/$1/;

# build a name for the file on the server
$localFile =~ s/\\[^\s\\]+$//;
$localFile .= "\path\" . $strFilename;
 
# save the file
open (OUTFILE,">$localFile");
binmode($fileup);
binmode(OUTFILE);
while ($bytesread=read($fileup,$buffer,1024)) {
print OUTFILE $buffer;
}

I think the problem with the URLEncode function you posted is that it doen't encode the file and other parameters in a multipart format.

I've found it frustrating, and surprisingly dificult, trying to send and recieve a file over HTTP using VB and ASP (I couldn't get the posting acceptor to work, so I switched to perl). My background has more perl and it seems much esier to do things that way.

Thanks for the help so far, but I think vmv's approach might workout faster, any pointers on that method?
Have you read this from microsoft:

HOWTO: Use the PostData Parameter in WebBrowser Control
http://support.microsoft.com/support/kb/articles/Q174/9/23.ASP?LNG=ENG&SA=MSDN&FR=0 
 
By the way, you never said... Are you using Service Pack 3???  If not, you will need to install it...


Cheers!®©
ASKER CERTIFIED SOLUTION
Avatar of mcrider
mcrider

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi mcrider,

Sorry it's been a while, well I've nearly got your answer working, I've found documentation on how to encode the file.

The only trouble is that the header parameter gets trashed between the Navagate method and the beforeNavagate2 event.  Any ideas?

Thanks
What do you mean... "Gets trashed"?
OK if my header for the navigate is "content-type: multipart/formdata; boundary=gc0p4Jq0M2Yt08j34c0p"

When I get to the beforeNavigate2 event the contents of the header reads something like "content-typcontent-type: multipart/formdata; boundary=gc0p4Jq0M2Yt08j34c0p"

the parameter is read only so I can't modify it.  I'll double check the service pack status later.

Well I finally got it to work.

I've worked around the trashing of the headers by adding a string of spaces to the end of the header. (BTW I do have SP3 installed)

When sending a binary file you have to read it into a byte array and then convert it to unicode and store it in a string (it makes life a bit easier) build up the rest of the string and then convert it back from unicode and store it in a byte array.

I had a bit more trouble with the perl script at the other end but found that if I put in two boundaries in a row then it would see the attachment.

Thanks again.

Any way here is the code:

Private Sub post_Click()
    Dim iHandle As Integer
    Dim sFile As String
    Dim sTemp As String
    Dim sHeaders As String
    Dim sBoundry As String
    Dim sPost As String
    Dim lSpot As Long
    Dim lFind As Long
    Dim bTemp() As Byte
   
    sFile = "H:\1.jpg"
    iHandle = FreeFile
    Open sFile For Binary Access Read As iHandle
    If iHandle Then
        ReDim bTemp(LOF(iHandle)) As Byte
        Get #iHandle, , bTemp
        sTemp = StrConv(bTemp, vbUnicode)
        Close iHandle
    End If
   
    sBoundry = "AaBbQQbBaA"
   
    sHeaders = "Content-Type: multipart/form-data, boundry=" & sBoundry & _
               vbCrLf & "                                                                            "
   
    sPost = "-----------" & sBoundry & vbCrLf & _
            "-----------" & sBoundry & vbCrLf & _
            "Content-Disposition: form-data; name=""my_file""; filename=""" & _
            sFile & """" & vbCrLf & _
            "Content-Type: image/jpg" & vbCrLf & _
            "Content-Transfer-Encoding: binary" & vbCrLf & vbCrLf & sTemp & _
            vbCrLf & "-----------" & sBoundry & "--" & _
            vbCrLf & "-----------" & sBoundry & "--" & vbCrLf
   
    bTemp = StrConv(sPost, vbFromUnicode)
   
    WB.Navigate "http://server3/postingwork/Finish.pl", navNoReadFromCache, "", bTemp, sHeaders
       
         
End Sub
Thanks for the points! Glad I could help!


Cheers!®©