Uploading Files to the Web Server 1
Uploading a file to 4D requires a properly coded web page form, receiving the file in a 4D blob, and then extracting the file from the blob. One of the things you have to be careful of is exceeding 4D's memory limits when uploading a file. When a file is uploaded its entire contents are copied into a blob that exists in memory. If the file size is too large you run the risk of having 4D unexpectedly quit. 4D 2003 introduced a new Database Parameter that can be set to prevent this from happening. The default value for this new parameter is 2,000,000 bytes (just under 2MB) with a maximum value of 2,147,483,648 bytes (2GB) allowed.
You just need to call this once during application start-up to set the maximum size a web request can be (the value should be expressed in bytes):
SET DATABASE PARAMETER (Maximum Web requests size;value)
OK, so let's get to the code. First we need to code the web page and then we'll cover the 4D code that handles the upload. Here's the relevant html code from the web page:
<form name="uploadForm" method="post" action="/4dcgi/WebUploadFile_Submit" enctype="multipart/form-data"> <p>Select a file to upload and click the Submit button.</p> <p><input type="file" name="WU_xb_file" /></p> <p><input type="submit" name="submit" value="Submit" /></p> </form>
There are three things you will want to notice in this code. The first is the form's action parameter. You have to call 4D with either 4dcgi or 4daction, otherwise the Compiler_Web method created below will not get called. The second is the enctype parameter in the form tag. In the case of a form that will be uploading a file you must declare the enctype as multipart/form-data. This is to ensure that the browser sends the file to the server in the proper format. The third item of interest is the name of the file upload object; You will need to declare a blob variable with the identical name in 4D.
Now we'll move to 4D and create the code we need there.
Create a method named Compiler_Web if it doesn't already exist. In the Compiler_Web method declare the blob that will receive the file.
C_BLOB(WU_xb_file) `must match the name of the input object on the web page
When your form is submitted the Compiler_Web method will be called automatically and the blob WU_xb_file will receive the uploaded file.
Our final step is to extract the file itself from the blob. After the Compiler_Web method has fired the blob contains the file and some header information that is not part of the file. We can look at this header information to determine the file's name and content type. We then need to delete the header information from the blob so that only the file remains.
Here's the WebUploadFile_Submit code that gets called by the form on the web page:
C_TEXT(WU_t_fileName;WU_t_contentType) WU_t_fileName:="" WU_t_contentType:="" WebUploadFile_ParseBlob (->WU_xb_file;->WU_t_fileName;->WU_t_contentType) `The blob WU_xb_file now contains the file that was uploaded BLOB TO DOCUMENT(WU_t_fileName;WU_xb_file) ` the doc creator gets set to 4D by BLOB TO DOCUMENT. On Windows and ` OS X this will take care of it, on OS 9 you need to add code to determine ` the file type to set using the Content Type. SET DOCUMENT CREATOR(WU_t_fileName;"") $0:="index.html" `return the home page for this example
WebUploadFile_Submit calls WebUploadFile_ParseBlob to do the work of parsing the blob into usable values. First some variables are declared and the parameters are copied into local variables:
C_POINTER($1;$2;$3;$p_blobPointer;$p_fileName;$p_fileType) C_BLOB($xb_tempBlob) C_TEXT($t_blobText;$t_header;$t_contentType) C_LONGINT($l_contentTypePosition;$l_headerEnd) C_LONGINT($l_blobSize;$l_sourceOffset;$l_destinationOffset) $t_blobText:="" $t_header:="" $t_contentType:="" $l_contentTypePosition:=0 $l_headerEnd:=0 $l_blobSize:=0 $l_sourceOffset:=0 $l_destinationOffset:=0 SET BLOB SIZE($xb_tempBlob;0) $p_blobPointer:=$1 $p_fileName:=$2 $p_fileType:=$3
Next the first 1k of the blob is copied into a temporary blob and then converted to text. This text will contain header information for the file that was uploaded:
`only the beginning of the file data is needed for parsing $l_blobSize:=BLOB size($p_blobPointer->) If ($l_blobSize>1024) $l_blobSize:=1024 End if COPY BLOB($p_blobPointer->;$xb_tempBlob;$l_sourceOffset; $l_destinationOffset;$l_blobSize) $t_blobText:=BLOB to text($xb_tempBlob;Text without length) SET BLOB SIZE($xb_tempBlob;0) `clear the blob
Now that the header is in a text variable the file's name and content type can be extracted:
`header ends after CRLFCRLF $l_headerEnd:=Position(Char(Carriage Return)+Char(Line Feed)+Char( Carriage Return)+Char(Line Feed);$t_blobText) If ($l_headerEnd>0) $t_header:=Substring($t_blobText;1;$l_headerEnd-1) $l_namePosition:=Position("filename=";$t_header) $l_contentTypePosition:=Position("Content-Type: ";$t_header) If ($l_namePosition>0) If ($l_contentTypePosition>0) $t_fileName:=Substring($t_header;$l_namePosition+Length("filename= ") ;$l_contentTypePosition-Length("Content-Type: ")-2) Else $t_fileName:=Substring($t_header;$l_namePosition+Length("filename= ")) End if ` ($l_contentTypePosition>0) `more cleanup for Netscape If (Position("%20";$t_fileName)#0) $t_fileName:=Replace string($t_fileName;"%20";" ") $t_fileName:=Replace string($t_fileName;Char(Double quote );"") End if Else $t_fileName:="Unable to determine" End if `($l_namePosition>0) If ($l_contentTypePosition>0) $t_contentType:=Substring($t_header;$l_contentTypePosition+ Length("Content-Type: ")) End if
Finally the header is deleted from the blob leaving just the file itself, and the file name and content type are returned in the passed parameters:
`file data itself is after CRLFCRLF sequence $l_sourceOffset:=$l_headerEnd+3 $l_destinationOffset:=0 DELETE FROM BLOB($p_blobPointer->;$l_destinationOffset;$l_sourceOffset) End if $p_fileName->:=$t_fileName $p_fileType->:=$t_contentType