Articles Archive
Articles Search
Director Wiki
 

Multimedia made to order

October 17, 2000
by Mark Daggett

In this brief article, I will demonstrate how to enrich a user's experience of your Shockwave games and applications by allowing them to upload their own images. This tutorial will take you through the basic steps of building a PHP script and Shockwave file that will:

  1. Upload an image to the server and store it to a specific directory;
  2. Create a dynamically generated document with the newly uploaded image as an externalParam for your Shockwave file; and
  3. Create a Shockwave file that will read the external param and load the image that the user just uploaded.

What can this script be used for?

This script could be used for lots of games or applications where a high degree of user control is desirable. Imagine a multi-user chat where users could upload their own avatar image before they entered a chat. Imagine a space invader game where your ex-girlfriend/boyfriend represents the aliens. Imagine a catalog interface where you can upload new products from a hidden page that allows you to avoid uploading the application all over again with the new images. Imagine a community-controlled scrapbook where users can upload images to share with each other. The uses for this code are only limited by the applications you are willing to apply it to.

What do I need to know before I read this tutorial?

This tutorial is very basic in terms of the actual Lingo involved in parsing the image out from the HTML and including it in the Shockwave file. However, while the PHP that is involved is not by any stretch of the imagination advanced, it may need some extra attention from those developers who are used to using Perl or just Director. However, in the long run PHP is very good for the junior programmer (like me) because it does not take a tremendous amount of investment to get very sophisticated results. This is what initially attracted me to Director, too. The real advantage of PHP is that it can be included with your normal HTML; so, it makes integration with the rest of the site and the development of immersive applications a snap. However, Zac wanted me to assure you all that everything I do with PHP can be done easily with another CGI language like Perl. Indeed, nothing that I demonstrate in this tutorial is impossible to do in another language (easily done, however, is another story).

Getting Started

You must have PHP installed on your server in order to use this tutorial. If you are not the server administrator for your site, ask your admin if PHP is currently installed on your server. In addition, you may need to fool around with the file extensions of your PHP files. In a recent tutorial that I read, all the tutorial files ended in the ".php" extension. However, my server is configured to use the ".phtml" extension. You may need to adjust your file extensions depending on your configuration.

What is PHP

This definition is borrowed from Zend.com's FAQ page description of PHP.

PHP (recursive acronym for PHP: Hypertext Preprocessor) is an open-source server-side scripting language (freely downloadable from php.net and zend.com) for creating dynamic Web pages for e-commerce and other Web applications. A dynamic Web page is a page that interacts with the user, so that each user visiting the page sees customized information. Dynamic Web applications are prevalent in commercial (e-commerce) sites, where the content displayed is generated from information accessed in a database or other external source.

Enough talk. I came for the code

Let's do some game planning for how the image upload application is going to happen. The whole process can be broken down into three basic steps.


  1. Create the HTML form that submits the image information to the PHP file for processing.

  2. The PHP file will check various properties of the file (size, type, name) and then upload them to the server or return an error to the user. After the image has been uploaded, the final HTML page with the Shockwave file will be generated and the image location will be passed in as an externalParam to the Shockwave file.

  3. Once the Shockwave file has loaded, we will load the location of the image from information that was retrieved from the HTML page. Then the magic begins.

Step 1: <FORM> follows function

First, let's build the form elements of the HTML page that will allow the user to browse their hard drive for an image and submit it as a string to the PHP script. Call this script "index.html" since it will be the first page that the user encounters. Inside the <BODY> tag we will write the form elements.

<!-- start of upload form //-->
<BR>Select a .gif or .jpg to upload to the server. Your image can't be larger than 200k.

<FORM ACTION="upload.phtml" METHOD="POST" ENCTYPE="multipart/form-data">
<INPUT TYPE="HIDDEN" NAME="MAX_FILE_SIZE" VALUE="200000"> 
Submit this file : <INPUT TYPE="FILE" NAME="userfile"><BR>
<INPUT TYPE="SUBMIT"><BR>
</FORM>

This is all the code needed for the form elements on the "index.html" page. There are several things to take notice of:

The "ACTION" that is being called is to "upload.phtml"; this is the PHP file that we will write in step two. The input type on the "userfile" property is set to "FILE"; this will create a browse button on the HTML page that will allow the user to browse their hard drive for an image. Finally, notice that there is a hidden input type with a value of 200000. What this means is that any file submitted to the server that is larger than 200k will not be uploaded. You want your user to be realistic about the size of the file to be included, so this option lets you set a limit on the size of a possible file.

PHP is for me

Now the fun stuff starts. We want to start building the PHP script that will process the image and generate the final HTML page with the Shockwave file embedded in it. One of the coolest things about PHP is that it is scripted right into the HTML document. Unlike Perl or other CGI languages, it isn't necessary to call any other scripts to perform the functions nested in other documents. This is a real advantage in some cases because with fewer files to link up there is a smaller chance that you will link something incorrectly.

PHP is enclosed within these tags.

<?php
   
?>

If we created a helloworld.phtml page and included this tag in the document, it would print "hello world" to the browser window.

<?php
        echo( "hello world" );
?>

Since "upload.phtml" is going to be processed by the browser in a way similar to HTML, much of the "upload.phtml" document can be written in HTML. Start the script with this bit of code:

<HTML>
<HEAD>
<TITLE> uploaded file and Shockwave movie </TITLE>
</HEAD>
<BODY bgcolor = "ffffff" text = "000000" Link = "660000" vlink = "440000">

This is all very straightforward HTML. Now the first lines of PHP are added.

<!-- start image parsing and generate the object and embed tags for the .dcr //-->
<?php
  //check to see if file was passed from the last form.
  if($userfile == "none"){
  echo("<B>File Size was too large,<BR>or you did not submit a file at all</B>");
  exit;
}

The code above checks to see if the variable "$userfile" is an empty value. If it is, we know that the index.html form did not pass an image string to this script. This could happen for one of two reasons:

  1. The user tried to submit an image that was over the 200k file limit; or
  2. The user hit the submit button without specifying an image to upload.

In either case, we don't want the script to continue, so we print out an error message to the screen and exit the rest of the PHP script. There are several things that I want to point out here. First, the "//" denotes a comment. This is analogous to the double hyphen in Lingo. Any text on the line that follows the "//" will not be processed in the PHP code. The next thing to notice is that we are using a basic if/then statement to check for the "$userfile" variable. In this case, if the variable is equal to "none", which means no value was passed, then: the Boolean statement will return true as a result; the echo statement will print to the screen; and the program will quit by calling the "exit" command. Finally, as in C or Perl, all your lines must end with a semicolon or you will get a parser error when you run the script.

After we have determined that the user has uploaded a file, the next stage of error checking is to establish whether the file is the correct file type. In the case of Shockwave, the file must be either a GIF or JPEG image, so those are the file types/formats to look for. Again, this will be framed inside of an if/then statement.

//check to see if image was a .gif or .jpg. or something else
if( $userfile_type == "image/jpeg" || $userfile_type == "image/gif" 
|| $userfile_type == "image/pjpeg"){

In this case, we are checking for server files types, so it is best to use the "or" statement in conjunction with the if/then code. In PHP, as with many other languages, "or" is represented with the double pipe symbol. In Lingo, "=" sometimes means "equal to". In PHP, "equal to" is represented as "==". So the code is asking whether $userfile is equal to a .jpeg, a .gif, or a .pjpeg (which is another form of JPEG files that users may upload). If the uploaded file equals any of these file types, then we proceed to the next step, which is uploading it to the server.

In case you're wondering how PHP "knew" the file type of the uploaded image, it's a built-in PHP function. By attaching the "_type" to the "$userfile" variable, we can ask PHP to examine the file type. This is very similar to writing "put sprite($userFile).member.type". In Lingo, "type" may return "#bitmap"; and in PHP, "_type" may return "image/jpeg".

Now that we know the file exists and is of the right type, we need to upload it to the server and save it to a specific location so that we can get to it later. We will include these new lines of PHP:

//attempt to copy file to the directory.
if ( copy( $userfile, "$userfile_name" ) ) {
  echo("file Name: " . $userfile_name . "<BR>\n");
  echo("file Size: " . $userfile_size . "<BR>\n");
  echo("file Type: " . $userfile_type . "<BR>\n");

This uses the "copy" command to store the file to the server. In this case, the image is stored to the same directory as the one from which the script is running, but you could store it to a different directory without much difficulty. The "copy" command is inside an if/then statement because if there were an error trying to store the file, we would not want to continue the script. Once the image has been stored to the server we will echo various elements of the image back to the screen. Notice that there are new properties, other than type, that we have access to: "_name", used to get the filename; and "_size", to get the file size. We have also added some new code to the mix. The "\n" will make a new line in HTML. It is only a visual break, however, and will not actually break your line to the next line in processed HTML. For that you will still need to use the <BR> tag.

Now that we know the file has been successfully uploaded to the server, it is time to include the <OBJECT> and <EMBED> tags that will load the Shockwave file. Include this code:

echo(" ".
"<object classid=\"clsid:166B1BCA-3F9C-11CF-8075-444553540000\" \n" .
"codebase=\"http://download.macromedia.com/pub/shockwave/
cabs/director/sw.cab#version=8,0,0,0\" \n " .         
"ID=import-image width=240 height=240> \n" . 
"<param name=src value=\"import-image.dcr\"> 
\n" .  "<param name=swRemote value=\"swSaveEnabled='true'
swVolume='true' swRestart='true' swPausePlay='true' swFastForward='true' 
swContextMenu='true' \"> \n"  . 
"<param name=swStretchStyle value=none> \n" .  "<PARAM
NAME=bgColor VALUE=#FFFFFF> \n"  . "<param name=sw1 value=" .
$userfile_name . "> \n"  . 
"<embed src=\"import-image.dcr\" bgColor=#FFFFFF  width=240 height=240
sw1=\" " . $userfile_name . " \"
swRemote=\"swSaveEnabled='true' swVolume='true' swRestart='true' swPausePlay='true'
swFastForward='true' swContextMenu='true' \" \n"  . " swStretchStyle=none
type=\"application/x-director\"
pluginspage=\"http://www.macromedia.com/shockwave/download/\"></embed>
\n"  .  "</object> \n" .
"");

There are two new additions to the PHP code that I want to bring up now. The first is concatenation. In Director, two strings are concatenated with "&". In PHP, "." is used. Notice that the echo(); tag starts on one line and ends much further down the page, after all the object and embed elements have been added. Now, you may be saying, "Hey, smart guy; you told me every line of code needs to end with a semicolon. I see lots of code here that ends with a period instead." Well, you and I see several lines; however, PHP sees several strings that have been concatenated together using the period. To PHP, it is actually just one string. It is a good idea to concatenate script that is too long to fit on one line, so that your code is easier to read and easier to debug.

And I already see your next question, "How does PHP know when a period indicates concatenation, and when a period is just a period?" The answer is that PHP will only use a period as a concatenation character if it is placed outside quotation marks. The quotation marks are what PHP uses to validate text as a string. If you want to include a quotation mark in your string, you need to "escape" PHP processing of the quotation mark. Notice in the code above that in several places I have written "\". The backslash is an escape character that tells PHP to read the quotation mark as a string and not as a line of code.

The final addition to this code comes right after the "sw1=" portion of the code. You will see that I ended the string and then included the $userfile_name variable.

In PHP it is written:

sw1=\" " . $userfile_name . " \"  -- in Netscape 
"<param name=sw1 value=" . $userfile_name . "> \n"  -- in I.E.

The output to the screen will look like:

<param name=sw1 value="image.gif "> 

Remember that this code all happened inside of an if statement that was used to check if the upload to the server was successful. If the upload failed, we need to add a check to display this failure to the user.

} else {
    echo("<B> Error: Failed to copy file...");
        }

Regardless of whether the upload was a success or not, after we are done with the file we need to destroy its link to PHP to recover some system resources. By destroying the link, we are not deleting the newly uploaded file; we are just removing PHP's access to it.

//Destroy the file now that we have copied it.
unlink($userfile); 

The last part of the script is the final portion of the error checking. The script above was nested in an if/then statement that was checking for specific file types. If the user uploaded an incorrect file type, then we need to output the error to the screen along with information on the file that they attempted to upload. Finally, we need to close out the PHP script with the closing "?>" tag.

} else {
  //not a .gif or .jpg image
  echo("<B>Not a .gif or .jpg image.</B>");
  echo("file Name: " . $userfile_name . "<BR>\n");
  echo("file Size: " . $userfile_size . "<BR>\n");
  echo("file Type: " . $userfile_type . "<BR>\n");
      }
?>
</BODY>
</HTML>

Set Shockwave to upload

Now that the file has been created and the image location has been stored in the HTML as an externalParam, we need to uplaod that into the Shockwave movie. Here is the code for the movie script& that will load the image in from the externalParam.

global gImg -- this is the image name that we are trying to parse in

on prepareMovie me

  if the runMode = "author" then
    
    --This will not parse the image in from authoring mode.
    exit
    
  else
    
    if externalParamCount() > 0 then
      
      if externalParamName ("sw1") = "sw1" then
        
        gImg = externalParamValue ("sw1")
        gImg = string( the moviePath & gImg )
        
        repeat with x = 1 to the number of chars of gImg
          
          if char x of gImg = " " then
            delete char x of gImg
          end if
          
        end repeat
        
        member("externalImg").text = "Your external image is located:" & RETURN & gImg
        
      end if
      
    end if
    
  end if

end

In this movie script we run the whole routine in the prepareMovie handler. The first thing we do is check to see if it is being run in the authoring environment. This script will not run correctly in the authoring environment since it depends on external information nested in the HTML. We then run an if/then statement to count the number of externalParams. If it is larger then 0, it is OK to proceed to the next stage. The next if/then statement looks for a string value equal to "sw1" (which, you will notice, is the same value we use in the HTML). The value of "sw1" will be the name of the image uploaded to the server.

We now need to get a path to the image so that we can download it to the Shockwave movie. In this example, all the images that have been uploaded are in the same directory as the .dcr file, so by calling the moviePath property and concatenating it to the gImg we get a full-path URL to the image. We then run a repeat loop to strip out any whitespace characters that might have been passed in.

The final script that we need to build is the actual script that downloads the image into memory and assigns it to a placeholder cast member. This behavior script is applied to frame 1 of the movie.

global gImg


property pNetID
property pPlaceHolder
property pURL
property pImgSprite

on beginSprite me

  pImgSprite = 1
  pPlaceHolder = "placeHolder"
  pNetID = 0

  -- the url has the ticks on the end because shockwave will
  -- cache the graphic otherwise.

  pURL = gImg & "?" & the ticks
  pNetID = preloadNetThing(pUrl)

  sprite( pImgSprite ).loch = -999

end


on exitFrame me

  if the runMode = "author" then
    exit
  end if

  if pNetID > 0 then
    
    if netDone(pNetID) then
      member("externalImg").text = string( netError(pNetID) )
      set pNetID = 0
      member(pPlaceHolder).fileName = string(pURL)
      sprite( pImgSprite ).loc = point( the stage.rect.width /2 , the stage.rect.height /2 )
    end if
  end if

  go to the frame

end

This final script is also very straightforward. We are using the beginSprite to set the pNetID to download the image from the server. Then, in the exitframe we check the status of the process. As soon as the image has been downloaded, the pNetID will return a TRUE when queried by the netDone function. Once this happens we set pNetID back to 0, which in effect turns off the looping statement. Then we set the filename of the placeholder member to the string of pURL; since the image has already been downloaded, the image will replace the placeholder image. As a finishing touch, we will center the image on the Stage.

I have included a slightly snazzier version of this code ported into Barry Swan's TED 3D engine. This should give you a better idea of the code's possibilities.

You can view the code in action here or download a copy of the Director 8 movie and PHP code Mac or Windows.

Mark is a practicing artist and writer living in Southern California. He has been running a little site called FlavoredThunder (http://www.flavoredthunder.com) for the past 4 years. His work has appeared on Wired's Web Monkey site, Rhizome and ZKM. Commercially, Mark has produced games and DVDs for Major motion pictures including: "The Matrix", "Austin Power The spy who shagged me", "Pokemon the First Movie", "Chicken Run", "The Exorcist", "The Replacements","Deep Blue Sea" and "The Perfect Storm".

Copyright 1997-2024, Director Online. Article content copyright by respective authors.