ActionScript - loading and saving data
Contents
About
There are a number of ways Adobe Flash can save data, but in this article, we focus on the use of Action Script 3 (AS3) to upload and download data via HTML POST requests to server-side pages. I started this article when I had the need to use AS3 to upload and retrieve dynamically generated images and text to a database via PHP pages. Searching the web I found a huge number of posts - some better than others, so here I intend to consolidate some of the best solutions I have found.
WARNING: To get these examples working you will need a recent version of Adobe Flash (version 9 or above) and make sure you create projects with ActionScript 3.0. To test successful uploading you will need access to a local or remote PHP sever... preferably 4.1 or later. The PHP scripts below all try to save images to the same directory as the PHP page, meaning you should set the permission of your remote directory appropriately. If in doubt write-all and execute-all will work. I recommend Adobe Dreamweaver to edit and upload your PHP pages. If you have an older PHP version 4.0 or earlier use $_HTTP_POST_FILES instead of $_FILES.
Uploading a local file using 'FileReference.upload()'
If all you need to do is upload an (existing) image file from your desktop the solution is quite easy - you can use the built-in FileReference class and call FileReference.browse() to locate the file, then FileReference.upload() to upload the image to your desired CGI page.
INSTRUCTIONS: To get this code working you can create a new AS3 Flash file, add a single button called "button" and add the action script to the first frame of the timeline.
The ActionScript is:
var fileRef:FileReference = new FileReference();
fileRef.addEventListener( Event.SELECT, uploadFile );
fileRef.addEventListener( ProgressEvent.PROGRESS, fileUploadProgress );
fileRef.addEventListener( Event.COMPLETE, fileUploadComplete );
button.addEventListener( MouseEvent.CLICK, browseForFile );
// NOTE: remove the line above and write "browseForFile(null);" if you don't want to add a button.
function browseForFile( e:Event ):void
{
var imgTypeFilter:FileFilter = new FileFilter("Image Files (jpg,png)","*.jpg;*.png");
var allTypeFilter:FileFilter = new FileFilter("All Files (*.*)","*.*");
fileRef.browse([imgTypeFilter,allTypeFilter]); // NOTE: including these filters is optinal.
}
function uploadFile( e:Event ):void
{
var vars:URLVariables = new URLVariables();
vars.user_name = 'andrew'; // NOTE: you can put any number of different POST variables here.
var urlRequest:URLRequest = new URLRequest("http://yourdomain.org/uploadimage_fromfile.php");
urlRequest.method = URLRequestMethod.POST;
urlRequest.data = vars;
fileRef.upload( urlRequest, "file_upload", false );
}
function fileUploadProgress( e:ProgressEvent ):void
{
trace( ( e.bytesLoaded / e.bytesTotal ) * 100 );
}
function fileUploadComplete( e:Event ):void
{
trace( "upload complete" );
}
And the matching PHP page ("uploadimage_fromfile.php") is:
var fileRef:FileReference = new FileReference();
<?php
$user_name = $_POST['user_name'];
$target_path = "uploads/" . basename( $_FILES[ "file_upload" ][ "name" ] );
if ( move_uploaded_file( $_FILES[ "file_upload" ][ "tmp_name" ], $target_path) )
echo( "file upload success<br />... thanks ".$user_name );
else
echo( "error uploading file<br />" );
?>
This example also demonstrated how extra text variables (in this case a 'user_name') can be easily posted along with this image using the URLVariables class.
Acknowledgements: The code in this example was modified from code pasted by dotmini in a stackoverflow thread (see origional code on stackoverfow). |
Uploading binary data using the ByteArray and URLLoader classes
If the image you wish to upload isn't save to disk, but instead generated within your code (eg: taking a screenshot), the code is a bit trickier - you will need to use the ByteArray class to generate your binary data and the URLLoader class to upload it. The example below uploads a JPEG screenshot of your main Flash interface.
INSTRUCTIONS: This example uses the well known "as3corelib" by Mike Chambers to encode BitmapData into a JPEG as a ByteArray. To get this working you must first go to as3corelib, download a .zip, and then put the "com" folder (under "src") into the same folder as your .fla file containing the AS below in the first frame. You'll probably also want to scribble a line in your clip so your saved file isn't plain white.
The ActionScript is:
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.utils.ByteArray;
import com.adobe.images.JPGEncoder; // You must download this from: https://github.com/mikechambers/as3corelib
var uploadPage:String = "http://yourdomain.org/uploadimage_frombytearray.php";
var bmpd:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
bmpd.draw(stage); // Takes a screenshot of stage.
var jpgEncoder:JPGEncoder = new JPGEncoder(80); // Instance of JPGEncoder with save quality between (0-100).
var byteArray:ByteArray = jpgEncoder.encode(bmpd); // Use the JPGEncoder to generate a ByteArray of the screenshot.
var req:URLRequest = new URLRequest(uploadPage + "?name=test.jpg"); // NOTE: here we can send a file name via "GET"
req.requestHeaders = new Array(new URLRequestHeader("Content-Type", "image/jpeg")); // so server knows it is getting an image
req.contentType = "image/jpeg"; // Specify content type.
req.method = URLRequestMethod.POST; // Set the method to POST.
req.data = byteArray; // Sets our data to the screenshot.
var loader:URLLoader = new URLLoader(); // Create a URL loader to send data to the server.
loader.dataFormat = URLLoaderDataFormat.BINARY; // Says that the content type is binary.
loader.load(req); // starts the download.
And the matching PHP page ("uploadimage_frombytearray.php") is:
<?php
if ( isset ( $GLOBALS["HTTP_RAW_POST_DATA"] ) && isset ( $_GET['name'] ) ) {
$fileName = $_GET['name']; // The image file name
$im = $GLOBALS["HTTP_RAW_POST_DATA"]; // Get the binary stream
// Write it:
$fp = fopen($fileName, 'wb');
fwrite($fp, $im);
fclose($fp);
echo ("file written successfully");
}
else
{
echo ("error writing file - header data set incorrectly" );
}
?>
Notice that this example passes a "name" for the file in the query string (GET). Sadly when you POST an image like this it isn't easily possible to use these classes to POST extra text values, nor to POST more than one images at time - even though these classes write out request using multipart/form-data.
Acknowledgements: The code in this example was modified from an excellent post at cultcreative.com which you can read here. |
Extending the URLLoader class to upload multiple binary files
For those who are keen, there are a few nice workaround to upload multiple binary files at once. Searching the web there are several people who have coded solutions, the best I've found is "[as3urlrequestbuilder http://blog.mikestead.me/upload-multiple-files-with-a-single-request-in-flash/]" by Mike Stead - a set of two simple classes, "URLFileVariable" and "URLRequestBuilder", which integrate with URLVariables and URLRequest. In the code below I use this library to save two PNG files with the second one representing an clone of the first which has been inverted.
INSTRUCTION: To install Mike's library go to here, download the .zip and move the "co" folder (under "src") to the same directory as your fla file. Notice it also requires the "as3corelib" library from the example above to call "PNGEncoder".
The ActionScript is:
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.utils.ByteArray;
import com.adobe.images.PNGEncoder; // Download this from: https://github.com/mikechambers/as3corelib
import co.uk.mikestead.net.URLFileVariable; // Download from: https://github.com/mikestead/as3urlrequestbuilder
import co.uk.mikestead.net.URLRequestBuilder;
var bmp1:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
bmp1.draw(stage); // Takes screenshot of stage.
var bmp2:BitmapData = bmp1.clone(); // Create a clone of this image to modify.
//## INVERT THE COLOR OF EACH PIXEL IN "bmp2":
for(var ix:Number=0; ix<=bmp2.width; ix++)
{
for(var iy:Number=0; iy<=bmp2.height; iy++)
{
var color:uint = bmp2.getPixel(ix,iy);
color = 255 - color;
bmp2.setPixel(ix,iy, color);
}
}
var byteArray1:ByteArray = PNGEncoder.encode(bmp1); // |- Use the PNGEncoder to generate ByteArrays
var byteArray2:ByteArray = PNGEncoder.encode(bmp2); // | of our BitmapData images.
//## CREATE VARIABLES TO SEND OFF TO THE SERVER:
var variables:URLVariables = new URLVariables();
variables.userName = "mike";
variables.image1 = new URLFileVariable(byteArray1, "image.png"); // Use this class for any binary files.
variables.image2 = new URLFileVariable(byteArray2, "image_inverted.png");
//## BUILD THE MULTIPART ENCODED REQUEST AND SET THE URL TO SEND IT TO:
var req:URLRequest = new URLRequestBuilder(variables).build();
req.url = "http://yourdomain.org/uploadimages_multiple.php";
//## CREATE THE LOADER AND TRANSMIT THE REQUEST:
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, onServerResponse);
loader.load(req);
//## HANDLE THE RESPONSE:
function onServerResponse(event:Event):void
{
var loader:URLLoader = URLLoader(event.target);
trace("onServerResponse: " + loader.data);
} // Starts the download.
And the matching PHP page ("uploadimages_multiple.php") is:
<?php
$filesOK = "";
$filesFault = "";
//## RUN THROUGH EACH UPLOADED FILE AND TRY PUTTING IN THE CURRENT DIRECTORY
//## WITH THE CORRECT FILE NAME:
foreach ($_FILES as $id => $details)
{
$targetPath = basename($details['name']);
if (move_uploaded_file($details['tmp_name'], $targetPath))
{
$filesOK .= "- " . $id . " : " . $details['name'] . "\n";
}
else
{
$filesFault .= "- '" . $details['name'] . "'\n";
}
}
//## RUN THROUGH EACH POST VARIABLE RECEIVED:
$postVars = "";
foreach ($_POST as $id => $value)
{
$postVars .= "- " . $id . " : " . $value . "\n";
}
//## PRINT OUT THE RESULTS:
if ($filesFault != "")
print "Files which could not be saved on server:\n" . $filesFault . "\n\n";
if ($filesOK != "")
print "Files successfully uploaded:\n" . $filesOK . "\n\n";
if ($postVars != "")
print "POST variables uploaded:\n" . $postVars . "\n\n";
?>
Acknowledgements: The code in this example was modified from code by Mike Stead from his article here. His original form handler php code also retrieves GET variables and prints everything to a log.txt you must create in the same directory. |
There are other people who've coded their own solutions too:
- [multipart-loader http://www.neerfri.com/2007/12/flex-multipartform-data-post-request.html] - a collection of about five classes which are similar to "UrlRequest", written by Neerfri (download). I had a bit of trouble getting it working, but others have given it high praise.
Links
- Adobe LiveDocs - Working with file upload and download - has a nice long explanation of how AC3 and HTTP POST request work.