Kevin has posted 5 posts at DZone. View Full User Profile

How to Use Flash for File Upload with PHP

08.11.2010
| 32539 views |
  • submit to reddit

User contributed content; it is core to the web.  From Flickr to YouTube, uploading your own content in order to share it to a broader audience is an activity that happens around the world, around the clock.  The task is not without its complexities however, and it seems to be done different ways for every site - sometimes with more success than others. 

This article will cover how to upload content to PHP, first from an HTML form, and then from a Flash-based form.  We will explore the challenges you will encounter, and propose solutions with an emphasis on the client. 

Download the associated source code for this article (~8KB)

 

HTML Form with File Upload

To upload content from an HTML page, you first need an HTML page.  HTML provides a number of constructs to present an upload form, most of which are designed to fall inside of a FORM tag.  When you want to upload content from an HTML form, you need to keep certain attributes in mind.

The “action” attribute of the FORM tag should point at the PHP page that will handle the actual upload.  We will see an example of this later in the article.  The “method” used to send a file should be “POST”.  You might choose to put a “name” value on the form if you intend to add any additional scripting.  Finally, and most importantly, is the “enctype”  which should be set to “multipart/form-data”.

<form
action="upload.php"
method="post"
enctype="multipart/form-data"
name="frmupload">

<p>
Name:
<input type="text" name="txtname" id="txtname">
</p>

<p>
Description:<br>
<textarea
name="txtdesc"
id="txtdesc"
cols="45"
rows="5"></textarea>
</p>

<p>
Image file:
<input type="file" name="imgfile" id="imgfile">
</p>

<p>
<input
type="submit"
name="btnsubmit"
id="btnsubmit"
value="Submit">
</p>

</form>

 

You can use any other form elements inside the FORM tag that you might have used in the past to send along additional information. Most of these will take the shape of an INPUT, and the file selection field is no different.  To give the user a chance to select a file, you specify an INPUT tag with the “type” attribute set to “file”.  The “name” attribute is important here as you will need it to reference the file content once it arrives at PHP on the server.

 

PHP Script to Receive the File

You might be inclined to think that handling an incoming file is a hard task requiring a lot of PHP code.  The basics however can be achieved with just a few lines of code.  The incoming file will be stored in the server’s temporary directory as defined by the “php.ini” file.  Once the request is complete, the file will be deleted.  This means that as the file comes in that you first need to move it to a more permanent location.

<?php

$uploaddir =
$_SERVER['DOCUMENT_ROOT'] .
'/upload/images/';
$uploadfile =
$uploaddir .
basename( $_FILES['imgfile']['name'] );

move_uploaded_file(
$_FILES['imgfile']['tmp_name'],
$uploadfile );

print "Got:<br/>";

foreach( $_POST as $ind => $val )
print $ind . ': ' . $val . '<br/>';

print "Upload complete.";

?>

 

Where you decide to move the file can mean a slow start on learning how to handle file upload, or a quick success.  You can only move the file to where the PHP server has access, and you will want to make sure that path exists in the first place.  Check the permission of your PHP server, and of the destination directory to make sure they can all talk to one another.

The incoming file, and some various pieces of information about it are stored in the PHP $_FILES associative array.  As you might expect by the term “array” PHP can handle multiple file uploads in a single form.  To access the files in the array, you use the name you gave the field in the HTML form, which is why that step was so important.  You could alternatively choose to iterate over the incoming files using a “for each” construct as well.

At this point then we have one or more incoming files, and we know how to access information about them.  We also know that we need to move them if we want the files to stick around for more than a single request. 

PHP provides the “move_uploaded_file()” function to move the file to its more permanent location.  The function takes two arguments.  The first argument is the file reference you want to move.  You will reference the associative array using the field name from the HTML form, and the “tmp_name” property to get the name of the file as stored in the PHP temporary directory.  The second argument is the absolute path, including the target name of the file, to where the file will be move.

That is all it takes.  The code above is all of fifteen (15) lines.  Getting a file from the client to the server couldn’t be easier.  If you have been a programmer for any length of time you will know however, that is really where the challenges start.  Reading the PHP manual pages on the “move_uploaded_file()” function will reveal many of those challenges.  We will take a look at some specific challenges next.

 

File Upload Challenges

NOTE: I’m going to be looking mostly at the client side of things - how the user interacts with the file upload operation.  It should be noted however that this example file upload code is just that, an example.  There’s a good deal of PHP/server security considerations to think about that I’m not going to cover.  Please think twice before deploying this exact code to a public server.

 

Page Refresh

A file upload, and in general most forms, will result in a page refresh.  Most users are accustomed to waiting for page refreshes these days, but that does not mean we should make them wait.  This problem is only compounded by file upload in that the user will have to wait on the same page for the file to upload, and only then get the new page in response - and have to wait for it to load as well.  Not an ideal user experience, and one that you need to make sure you account for by letting the user know what is going on.

Look and Feel

One of the first things you might run into is how to control the look and feel of the file selection control.  The problem is that the specific look and feel of the file selection control is managed by the browser implementation specifically.  That might not seem like a big challenge at first glance, but when you look at the how the control is rendered across operating systems, you start to get a feel for the problem.

File Size

How big of a file do you want your users to be able to upload?  This becomes a problem on the client because big files take time and networks are not always reliable.  It is also a problem on the server because the “htaccess” file will want to dictate what can be uploaded.  You don’t want the user to upload the file only to find that it was too big.  Or worse, have your server blow up because the user sent too large of a file.  This only gets compounded when users want to send more than one file at a time.

File Extenstion

While you can let the user select a file, finding information about that file on the client, before submitting is problematic.  A file extension does not mean you are getting the file you expected either.  It is entirely possible that you are expecting a JPEG file, but somebody with malicious intent has merely renamed an EXE to JPG.  An third-party EXE on your server simply invites a hacking attempt.

File Filtering

Thinking about file extensions begs the question of leveraging the file selection dialog to limit what file types are selected.  Unfortunately, browser do not offer a way to restrict file selection.  This means that somebody, even without malicious intent, may accidentally select an executable that might harm your infrastructure.  It also leaves a mental gap where you might want a JPEG but the user thinks they want to upload a ZIP archive of JPEG images.

Progress Indication

I mentioned before that file size is a challenge to be aware of when uploading files.  Users themselves also get impatient.  Most Internet providers give high download speeds, but slower upload speeds.  The traditional way to solve this is to disable the submit button to keep users from sending their content twice.  It would be far more efficient to let the users know the progress of their upload, but again, browsers do not provide access to progress information.

Multiple File Selection

Browsers also do not provide a file selection dialog that lets you choose multiple files at once.  If you want the user to be able to select multiple files, then you have to provide multiple HTML INPUT fields for them.  This means users have to repeatedly open the file selection dialog box and may inadvertently select the same file twice.  Going back to the file size issue again, you may also not want to let the user send all those files with one form, so the general solution is to limit the number of input fields.

 

File Upload Solutions in a Flash

It seems then that the browser file upload process is riddled with challenges.  It is, and indeed, this is why developers tend to struggle so much with this topic.  The good news is that I have not led you this far only to abandon you without answers.  And there are answers to all of these challenges.  Specifically, using the Adobe Flash Player gives you a means to solve all these problems in one shot.

Page Refresh

When you use Flash for forms and file uploads, the only reason the page has to refresh is because you want it to.  By default the Flash Player will send the relevant data, in this case one or more files, and then present the developer with an event to let them know when the server has finished processing and returned as response - you can even gracefully handle when a file upload fails in your application logic.

protected function doSubmitClick( event:Event ):void
{
var dest:URLRequest = new URLRequest( UPLOAD_URL );
var params:URLVariables = new URLVariables();

params.txtname = txtname.text;
params.txtdesc = txtdesc.text;

dest.method = URLRequestMethod.POST;
dest.data = params;

file.addEventListener(
ProgressEvent.PROGRESS,
doFileProgress );

file.upload( dest, IMAGE_FIELD );
}

 

Look and Feel

When it comes to Flash, you get control over how everything looks.  So much so that it can be intimidating at first.  Luckily, there’s designers in the world.  And if a designer isn’t available for you, there’s frameworks like Flex which is free and open source.  The control over look and feel doesn’t stop at file upload.  What you want the field and file selection button to look like is entirely up to you.  The following is an example of selecting a file with Flash.

<!-- Skin this to look like anything -->
<s:Button
x="207"
y="232"
label="Browse"
click="doBrowseClick( event )"/>

. . .

 

protected function doBrowseClick( event:Event ):void
{
if( file == null )
{
file = new FileReference();
file.addEventListener( Event.SELECT, doFileSelect );
file.addEventListener(
DataEvent.UPLOAD_COMPLETE_DATA,
doFileComplete );
file.addEventListener( Event.COMPLETE, doLoadComplete );
}

file.browse( [new FileFilter( "Image File", "*.jpg" )] );
}

 

File Size

The main class is FileReference, which gives you far more than just the ability to select a file.  We will talk more about that later, but initially our concern was of the size of file, or files, that the user has selected.  In the case of FileReference, there is a property that will tell you the size of the file in bytes.  Keep in mind that one megabyte is not exactly 1,000,000 bytes - it is 1,048,576 bytes.  Do some math and you can tell the user that their file is too big before they send it.

protected function doFileSelect( event:Event ):void
{
if( file.size < TWO_MEGABYTES )
{
inRange = true;
}
}

 

File Extension

If an extension is just a set of characters - characters that may mask something more malicious, or just plain incorrect - then you probably want to be able to take a look at the file contents.  Flash Player allows you to load local file contents and take a look.  Is the file the user selected a JPEG?  Load the bytes of the file, take a sampling and know for sure that it is not an executable in an image’s clothing.

protected function doLoadComplete( event:Event ):void
{
var marker:String = null;

marker = file.data.readUnsignedByte().toString( 16 );
marker = marker + file.data.readUnsignedByte().toString( 16 );

if( marker.toUpperCase() == JPEG_MARKER )
{
isJpeg = true;
} else {
isJpeg = false;
}
}

 

File Filtering

Using local file access you can now tell for sure what type of file the user is sending you before they submit the content.  That doesn’t mean that we shouldn’t make it easier for our users to select the right file in the first place.  When using the FileReference.browse() method, you can pass an array of FileFilter objects.  The result is a dialog box that reduces the clutter of all those files you don’t want to have selected.  No accidentally selecting a ZIP archive of images in place of a series of actual image files.

protected function doBrowseClick( event:Event ):void
{
if( file == null )
{
file = new FileReference();
file.addEventListener( Event.SELECT, doFileSelect );
file.addEventListener(
DataEvent.UPLOAD_COMPLETE_DATA,
doFileComplete );
file.addEventListener( Event.COMPLETE, doLoadComplete );
}

file.browse( [new FileFilter( "Image File", "*.jpg" )] );
}

 

Progress Indication

Because you have absolute control over a user interface that will render consistently back to Internet Explorer 6 and further, you can now easily and reliably enable and disable controls to keep users from trying to submit the form twice.  FileReference also fires off events as the file is being sent across the wire.  You also get notification on the client when the entire file has arrived at the server.  You can now reliably tell the user how far along their upload has progressed. 

It’s worth noting as well that you can get progress events when you are loading a local file as well, using the same code.

protected function doFileProgress( event:ProgressEvent ):void
{
var percent:Number = Math.floor(
( event.bytesLoaded / event.bytesTotal )
* 100 );

txtprogress.text =
event.bytesLoaded +
" of " +
event.bytesTotal +
" (" +
percent +
"%)";
}


protected function doFileComplete( event:DataEvent ):void
{
file.removeEventListener(
ProgressEvent.PROGRESS,
doFileProgress );

txtdesc.text = event.data;

isjpeg = false;
txtname.text = "";
imgfile.text = "";
btnsubmit.enabled = false;
}

 

Multiple File Selection

With Flash you can also select multiple files from a single dialog box without having to repeatedly open the dialog box, thereby requiring that the user keep a mental queue of what they have previously selected.  The FileReferenceList contains an array of FileReference objects the user has selected from their system. 

Because you can access size information on the selected files, you can now figure out just how big the total size of the upload will be, but here is where it gets interesting.  Flash Player allows you to upload single files from the list at a time versus sending all the files at once.  This allows you to effectively manage the upload process, inform the user, and control the impact to your infrastructure.

protected function doBrowseMulti( event:MouseEvent ):void
{
if( files == null )
{
files = new FileReferenceList();
files.addEventListener( Event.SELECT, doFilesSelect );
}

files.browse();
}

protected function doFilesSelect( event:Event ):void
{
var total:Number = 0;

for( var f:Number = 0; f < files.fileList.length; f++ )
{
total += FileReference( files.fileList[f] ).size;
}

total = total / ONE_MEGABYTE;

lbltotal.text = total.toFixed( 2 );
}

 

Conclusion

Flash Player is ubiquitous, and it sometimes gets ubiquitously poor treatment from the developer community.  Understandably, bad programmers do bad things with software, but the reality is that browsers do not implement everything you want or need to deliver an effective file upload experience (among other experiences).  And while HTML 5 is on the horizon, it too still does not cover the majority of these issues in its currently proposed form.

The browser plug-in API was developed for a reason - to provide functionality to the browsers where the standards have not.  Flash Player is a ubiquitous browser plug-in that you can use to make file upload tasks much easier for you, and more elegant for you users.  As the code snippets above demonstrates, it takes only a little effort to gather up a sizable amount of power.  And all along, the same PHP script remains unchanged.

AttachmentSize
Figure1.png14.97 KB
file-upload.zip8.29 KB
Published at DZone with permission of its author, Kevin Hoyt.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Sean Sandy replied on Wed, 2010/08/11 - 6:10am

Ummm.... " even an error response such as HTTP 200 can be handled gracefully." Really? http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_Success . Also, why would you do this over say, a hidden iframe/xhr upload?

Kevin Hoyt replied on Thu, 2010/08/12 - 12:36pm

Sean,

Ha!  Good catch.  I'll have the article updated.  When a file upload fails, and an HTTP status code is available, then Flash Player raises an event that can be handled via application logic.

http://bit.ly/aG4S1G

An IFRAME solves the page refresh, and potentially even handling an error (simply because the page won't be visible), but most of the other use-cases still provide value.  Notably:

- Complete control over the look and feel of the file field

- Determine file size on the client before the upload occurs

- Be able to check physical file before the upload occurs

- Control the file selection dialog to filter unwanted file types

- Easily inform the user of file upload progress

- Present a single dialog, once, to select multiple files

As an extension of checking the physical file, the ability to read the file selected by the user, in it's entirely, and process it on the client, without ever hitting the server, is pretty substantial.  This might give you the opportunity to display a selected image in the page, and perhaps even load it into CANVAS to let the user manipulate it futher before even uploading the image to the server.  Or perhaps the ability to process a data file entirely on the client for display in the application, without ever involving the server.  This is especially easy for text-based formats (XML, GPX, CVS, ICS, etc.).

If file upload is an occassional usage for your application, then it is entirely feasible that none of this matters.  For enterprise applications with heavy load however, reducing round trips to the server (especially just to echo the data back to the client), and additional server demands for file type checks, size requirements/limitations, etc. can present a significant cost savings.

Thanks for your comment,

Kevin

Igor Archangel replied on Sat, 2012/03/24 - 12:15am

The sample zip file is corrupted. How would I return data from Flash to PHP or .Net? I don't think Flash can save files unless you have an Adobe server.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.