The HTML form
The simplest design for a HTML form to support the upload of a file to a Web server can be as simple as having the file tag and a save button. As simple as it could get, such a form could look like this:
The HTML code to support the upload of a file should include the ENCTYPE parameter in the FORM tag which is needed to support that capability as well as a INPUT tag having a TYPE parameter having the value File.
Here is the code of this HTML page:
<STYLE> TABLE.LIST { BORDER: 1px #626262 solid; BORDER-COLLAPSE: collapse; Font-family: Arial, Helvetica, sans-serif; Font-size: 12px; } TABLE.LIST TD { BORDER: 1px #626262 solid; BORDER-COLLAPSE: collapse; FONT-SIZE: 12px; FONT-FAMILY: Arial; LINE-HEIGHT: 11pt; } TABLE.LIST TH { BORDER: 1px #626262 solid; BACKGROUND: #C7CDBA; FONT-SIZE: 12px; FONT-FAMILY: Arial; LINE-HEIGHT: 11pt; font-weight: bold } TD { Font-Size: 12px; Font-Family: "Arial"; Color: #333333; } TH { Font-Size: 12px; Font-Family: "Arial"; Color: #333333; } </STYLE> <TABLE CELLSPACING=0 CELLPADDING=2 Class=List BACKGROUND="http://www.utmag.com/January2006/MichelFournier1.jpg"> <TR> <TH>Upload a file from the browser <TR> <TD> <FORM METHOD=POST ACTION=http://www.myserver.com/... enctype="multipart/form-data" Style="Margin-Top: 8px; Margin-Bottom: 8px;"> Browse to select the file: <INPUT TYPE='FILE' SIZE=50 NAME='File'> <P Style="Margin-Top: 8px;"> <CENTER> <INPUT TYPE=Submit VALUE="Upload the file" Class=Button> </CENTER> </FORM> </TABLE>
The FileUpload object
I have defined a FileUpload object which allows me to set some property values, used for validation and save purposes, before calling the GetFile() method. The object is defined as followed:
How to call the method?
This article assumes that the process of the form submission has been received on the server side and that the control is now at the Visual FoxPro level. Thus, we are now ready to process this request.
Once the object created, we are ready to define the property values we need and call the GetFile() method. In this example, we are accepting a file no bigger than 100k, we want the file uploaded to be saved under d:\Images\City.jpg and the image can be either a GIF or a JPG.
LOCAL loGetFile,lcHtml loGetFile=CREATEOBJECT('FileUpload') loGetFile.nMaxSize=100000 loGetFile.cSavePath='d:\Images\City.jpg' loGetFile.lJPG=.T. loGetFile.lGIF=.T. IF NOT loGetFile.GetFile() lcHtml=loGetFile.cHtml ENDIF
The GetFile() method
The GetFile() method is quite interesting. While it is really short, it provides some great flexibility about basic validations we should expect in such upload capability in your application. This method includes a call to an object named Request to get the uploaded file. This is a West Wind Web Connection object. If you use any other approach, you might adapt that portion of code to fit your own needs. The method also includes some references to a function called GetMes(). This function is used to retrieve some messages in the proper languages. This is also easily adaptable to fit your own needs.
Unless you want to use some browser side approaches to apply some validations before allowing the upload, we should expect to have the main validation to take place on the server side. This method includes some basic validations to give you a good start.
Here is the code of that method:
* Save an uploaded file LOCAL lcFileName,lcFileBuffer,loImage,lcString,lcType,lcExtension,lnFormatAccepted LOCAL lcDestination lcFileName='' lcFileBuffer=Request.GetMultiPartFile(This.cField,@lcFileName) * File size This.nFileBuffer=LEN(lcFileBuffer) * File name This.cFileName=lcFileName * If the file is empty IF This.nFileBuffer=0 This.cHtml=GetMes('Upload','FileEmpty') RETURN .F. ENDIF * If the file size is bigger than the maximum size IF This.nFileBuffer>This.nMaxSize This.cHtml=GetMes('Upload','LimitExceeded') RETURN .F. ENDIF * If we use the same name as the one uploaded IF This.lUseSameName This.cSavePath=This.cSavePath+'\'+This.cFileName ENDIF * Now dump the file to disk STRTOFILE(lcFileBuffer,This.cSavePath) lnFormatAccepted=0 * If we have to verify for a specific type IF This.lJPG OR This.lGIF lcType=This.FileType(This.cSavePath) llAccept=.F. lnFormatAccepted=0 IF This.lJPG lnFormatAccepted=lnFormatAccepted+1 ENDIF IF This.lGIF lnFormatAccepted=lnFormatAccepted+1 ENDIF DO CASE CASE This.lJPG AND lcType='JPG' llAccept=.T. CASE This.lGIF AND lcType='GIF' llAccept=.T. ENDCASE IF NOT llAccept This.cHtml=GetMes('Upload','BadFormat') ERASE (This.cSavePath) RETURN .F. ENDIF ENDIF IF This.nWidth>0 OR This.nHeight>0 loImage=CREATEOBJECT('Image') loImage.Picture=This.cSavePath This.nImageWidth=loImage.Width This.nImageHeight=loImage.Height * If the width is not the proper size IF This.nWidth>0 IF This.nWidth<>This.nImageWidth This.cHtml=GetMes('Upload','BadWidth') This.cHtml=STRTRAN(This.cHtml,'##RequiredWidth##',ALLTRIM(STR(This.nWidth))) This.cHtml=STRTRAN(This.cHtml,'##Width##',ALLTRIM(STR(This.nImageWidth))) ERASE (This.cSavePath) RETURN .F. ENDIF ENDIF * If the height is not the proper size IF This.nHeight>0 IF This.nHeight<>This.nImageHeight This.cHtml=GetMes('Upload','BadHeight') This.cHtml=STRTRAN(This.cHtml,'##RequiredHeight##',ALLTRIM(STR(This.nHeight))) This.cHtml=STRTRAN(This.cHtml,'##Height##',ALLTRIM(STR(This.nImageHeight))) ERASE (This.cSavePath) RETURN .F. ENDIF ENDIF ENDIF * Adjust file extension when required IF lnFormatAccepted>1 lcExtension=RIGHT(This.cSavePath,3) IF UPPER(lcExtension)<>lcType lcDestination=SUBSTR(This.cSavePath,1,LEN(This.cSavePath)-3)+LOWER(lcType) IF FILE (lcDestination) ERASE (lcDestination) ENDIF RENAME (This.cSavePath) TO (lcDestination) ENDIF ENDIF
The GetFile() method makes use of a method named FileType(). The FileType() method is used to determine the graphical format of the uploaded image file. Thanks to Universal Thread members, including Tore Bleken, who provided the FileType() function. You can follow that thread on the Universal Thread by the use of thread ID 1040691.
Here is the code of the FileType() method:
* Returne the file type FUNCTION FileType PARAMETER tcData LOCAL lcReturn,lcString lcString=FILETOSTR(tcData) DO CASE CASE LEN(lcString)<4 lcReturn='' CASE LEFT(lcString,3)=CHR(0xFF)+CHR(0xD8)+CHR(0xFF) lcReturn='JPG' CASE LEFT(lcString,3)='GIF' lcReturn='GIF' CASE SUBSTR(lcString,42,3)='EMF' lcReturn='EMF' CASE LEFT(lcString,4)=CHR(0xD7)+CHR(0xCD)+CHR(0xC6)+CHR(0x9A) lcReturn='WMF' CASE LEFT(lcString,4)=CHR(0x4D)+CHR(0x4D)+CHR(0x00)+CHR(0x2A) lcReturn='TIF' CASE LEFT(lcString,4)=CHR(0x89)+'PNG' lcReturn='PNG' CASE LEFT(lcString,2)='BM' lcReturn='BMP' OTHERWISE lcReturn='' ENDCASE RETURN lcReturn
What's next?
This object is really small and provides some basic support for uploading a file from a browser to a Web server. However, it can be easily enhanced to support additional types of graphical formats and some additional validations.
This object also supports the upload of files having a certain size only. If you wish to upload big files, which will take several seconds to upload or, worst, would fire a timeout of the browser before the file finishes to upload, I would recommend to use another approach.
The mention in the above paragraph has nothing to do with the nMaxSize property. This property is simply there in cases where we know about the average size of file to expect. For example, I have a need to support the upload of members file on a specific Web site. I know the file size should be between 10 and 40k. To be sure, I can define the nMaxSize property to be 100k. Assuming such file received would be better than that, than it could be the user uploaded the wrong file or there is a really bad problem with the file.