![]() |
|
Uploading files with Flash 8
Uploading files with Flash 8
Export JPEG with Flash/PHP |
|
|
|
|
As you can see in the previous example, once "print" button is pressed a loader window appears counting the percent
progress.
This is because i used a setInterval function to copy all the pixels. This won't stress your cpu so much..
Here the class i use:
import flash.display.BitmapData; import flash.geom.Rectangle; import flash.geom.ColorTransform; import flash.geom.Matrix;
/** * Little and simple print flash screen class */ class it.sephiroth.PrintScreen {
public var addListener:Function public var broadcastMessage:Function
private var id: Number; public var record:LoadVars;
function PrintScreen(){ AsBroadcaster.initialize( this ); }
public function print(mc:MovieClip, x:Number, y:Number, w:Number, h:Number){ broadcastMessage("onStart", mc); if(x == undefined) x = 0; if(y == undefined) y = 0; if(w == undefined) w = mc._width; if(h == undefined) h = mc._height; var bmp:BitmapData = new BitmapData(w, h, false); record = new LoadVars(); record.width = w record.height = h record.cols = 0 record.rows = 0 var matrix = new Matrix(); matrix.translate(-x, -y) bmp.draw(mc, matrix, new ColorTransform(), 1, new Rectangle(0, 0, w, h)); id = setInterval(copysource, 5, this, mc, bmp); }
private function copysource(scope, movie, bit){ var pixel:Number var str_pixel:String scope.record["px" + scope.record.rows] = new Array(); for(var a = 0; a < bit.width; a++){ pixel = bit.getPixel(a, scope.record.rows) str_pixel = pixel.toString(16) if(pixel == 0xFFFFFF) str_pixel = ""; // don't send blank pixel scope.record["px" + scope.record.rows].push(str_pixel) } scope.broadcastMessage("onProgress", movie, scope.record.rows, bit.height) // send back the progress status scope.record.rows += 1 if(scope.record.rows >= bit.height){ clearInterval(scope.id) scope.broadcastMessage("onComplete", movie, scope.record) // completed! bit.dispose(); } } }
The ASBroadcaster class will be useful because i will use the addListener() method in order to receive events (onStart, onComplete and onProgress).
the main method is "print", which accepts there params:
- mc: the movieclip to copy
- x: x origin of the copy
- y: y origin of the copy
- w: width
- h: height
then the setInterval will copy all the pixel in one single row in the BitmapData object, every 5 ms seconds.
Every array of colors (in each row) will be stored in a new array within a LoadVar object. In this way i will have 210 variables to post with the LoadVars object.
| Please note that 0xFFFFFF pixels won't be added in the arrays, this beacuse the image i will create in PHP will have a blank background color, and for this reason blank pixels wont be created every time... |
3. The .fla source
Now let's take a look at the flash file will use this "PrintScreen" class:
import it.sephiroth.mloaderWindow import it.sephiroth.PrintScreen
var loader:mloaderWindow = this.createClassObject(mloaderWindow, "loader", 10, {_x:-1000, _y:-1000}) loader.setStyle("borderColor", 0x006699)
// listener which receives the broadcast message // from the PrintScreen class var listener:Object = new Object();
// copy in progress... listener.onProgress = function(target:MovieClip, loaded:Number, total:Number){ var perc = Math.round((loaded/total)*100) loader.label = "computing... " + perc + "%" loader.value = perc } // copy is complete, send the result LoadVars to PHP listener.onComplete = function(target:MovieClip, load_var:LoadVars){ loader.label = "sending to php..." load_var.send("files/pixels.php", "_blank", "POST") loader.close() }
/** * Print Button has been clicked */ function print_me(){ video_mc.pause() // first pause the playing video pn = new PrintScreen(); // initialize the PrintScreen class pn.addListener( listener ); // assign a listener pn.print(_root, 0, 0, 500, 210) // copy the _root loader.label = "computing... 0%" loader.open(true, true, true); // open a loader }
there's nothing particular to say here..
Once the "print" button is clicked call the print_me() function.
Stop the playing video, in order to copy the current video frame.
Initialize the PrintScreen class and assign a listener which will receive all the broadcaster messages.
I used pn.print(_root, 0,0, 500, 210) in order to print all the contents in _root.
Once the process is completed send the LoadVars object, which is returned by the onComplete function, to PHP using the POST
method ( the posted Content-length is: 563024 )
4. Generate the image in PHP
Here the code of the "pixels.php" page
<?php
error_reporting(0); /** * Get the width and height of the destination image * from the POST variables and convert them into * integer values */ $w = (int)$_POST['width']; $h = (int)$_POST['height'];
// create the image with desired width and height
$img = imagecreatetruecolor($w, $h);
// now fill the image with blank color // do you remember i wont pass the 0xFFFFFF pixels // from flash? imagefill($img, 0, 0, 0xFFFFFF);
$rows = 0; $cols = 0;
// now process every POST variable which // contains a pixel color for($rows = 0; $rows < $h; $rows++){ // convert the string into an array of n elements $c_row = explode(",", $_POST['px' . $rows]); for($cols = 0; $cols < $w; $cols++){ // get the single pixel color value $value = $c_row[$cols]; // if value is not empty (empty values are the blank pixels) if($value != ""){ // get the hexadecimal string (must be 6 chars length) // so add the missing chars if needed $hex = $value; while(strlen($hex) < 6){ $hex = "0" . $hex; } // convert value from HEX to RGB $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); // allocate the new color // N.B. teorically if a color was already allocated // we dont need to allocate another time // but this is only an example $test = imagecolorallocate($img, $r, $g, $b); // and paste that color into the image // at the correct position imagesetpixel($img, $cols, $rows, $test); } } }
// print out the correct header to the browser header("Content-type:image/jpeg"); // display the image imagejpeg($img, "", 90); ?>
That's all.
This is only a demonstration, because this method require a lot of memory to be used from PHP in order to generate the image (ImageMagik should be a better solution in this case indeed), and also because the POST headers are very very big!
You can use for example the Live HTTP headers FireFox extension to see exactly what we're passing to the pixels.php page.
In the same way you can read pixels informations from a BitmapData many other things can be done, for example parsing an image and find streets
6. Download file source
Download files used in this tutorial
|
Loading external JPGs into your main SWF movie
11/2/2003 - Welcome to the new Flash Remoting Library
Including external JPGs (or SWFs) in a Flash movie
infinite Duplicate MovieClips
Parsing XML files in PHP is complex. But, like most things, it's
not that hard once you get the hang of it. I don't know about you,
but I learn best by seeing actual code. So, here's a real world example.
If you're intereted in some more in-depth information on this subject,
take a look at a presentation I gave entitled
Parsing,
Validating and Saving Data from Complex XML Streams.
#! /usr/local/bin/php -q <?php
/** * A basic example of how to parse XML files. * * <p>Relies on PHP's {@link http://php.net/ref.xml SAX parser}.</p> * * <p>This example grabs stock quotes from NASDAQ's website. You tell * the program which stocks to check by adding/removing elements of the * <var>$Symbols</var> array. The script outputs a series * of fake SQL query strings.</p> * * <p>Output is placed into an associative array called <var>$Data.</var></p> * * <p>Element names become the array's keys. So: * <br /><code><TAG>The info</TAG></code> * <br />Would become: * <br /><code>$Data['TAG'] = 'The info';</code> * </p> * * <p>Where elements have attributes, the array's key is the name of * the element followed by ":" and the name of the attribute: * <br /><code><TAG ID="55">The info</TAG></code> * <br />becomes... * <br /><code>$Data['TAG'] = 'The info'; * <br />$Data['TAG:ID'] = 55;</code> * </p> * * <p>Do note, "case folding" is in effect, so lower case tag and * attribute names are converted to upper case.</p> * * <p>Requires PHP version 4.3.0 or later.</p> * * @author Daniel Convissor <danielc@AnalysisAndSolutions.com> * @copyright The Analysis and Solutions Company, 2002-2004 * @version 2004-02-11 11:00:00 * @link http://www.AnalysisAndSolutions.com/code/phpxml.htm
* @link http://php.net/ref.xml
*/
// ? // Which ticker symbols do you want to evaluate?
$Symbols = array('ET', 'EK');
// Set the base URI.
$URI = 'http://quotes.nasdaq.com/quote.dll?page=xml&mode=stock&symbol=';
// Make sure there's no other data with these names. $ParserProbs = array(); $DataProbs = array();
// Array to convert XML entities back to plain text. $XmlEntities = array( '&' => '&', '<' => '<', '>' => '>', ''' => '\'', '"' => '"', );
/** * Runs each time an XML element starts. */ function StartHandler(&$Parser, &$Elem, &$Attr) { global $Data, $CData, $XmlEntities;
// Start with empty CData array. $CData = array();
// Put each attribute into the Data array. foreach ($Attr as $Key => $Value) { $Data["$Elem:$Key"] = strtr(trim($Value), $XmlEntities); // debug // echo "$Elem:$Key = {$Data["$Elem:$Key"]}\n"; } }
/** * Runs each time XML character data is encountered. */ function CharacterHandler(&$Parser, &$Line) { global $CData;
/* * Place lines into an array because elements * can contain more than one line of data. */ $CData[] = $Line; }
/** * Runs each time an XML element ends. */ function EndHandler(&$Parser, &$Elem) { global $Data, $CData, $DataProbs, $Sym, $XmlEntities;
/* * Mush all of the CData lines into a string * and put it into the $Data array. */ $Data[$Elem] = strtr( trim( implode('', $CData) ), $XmlEntities);
// debug // echo "$Elem = {$Data[$Elem]}\n";
switch ($Elem) { case 'LAST-SALE-PRICE': // Make sure the data is clean. if ( !preg_match('/^\d{1,8}(\.\d{1,2})?$/', $Data[$Elem]) ) { // Make note of the error. $DataProbs[] = "$Elem has bad format: {$Data[$Elem]}"; } break;
case 'TRADE-DATETIME': /* * Ensure data is clean, plus save match parts to $Atom, * which will be used to convert date/time to MySQL format. */ if ( !preg_match('/^(\d{4})(\d{2})(\d{2}) (\d{2}:\d{2}:\d{2})$/', $Data[$Elem], $Atom) ) { // Make note of the error. $DataProbs[] = "$Elem has bad format: {$Data[$Elem]}"; }
$Data[$Elem] = "$Atom[1]-$Atom[2]-$Atom[3] $Atom[4]";
break;
case 'EQUITY-QUOTE': // Final item tag. Do something with the data.
// Make sure the data is clean. if ( !preg_match('/^\w{1,9}$/', $Data['EQUITY-QUOTE:CUSIP']) ) { // Make note of the error. $DataProbs[] = "$Elem has bad format: " . $Data['EQUITY-QUOTE:CUSIP']; }
/* * Double check that all of the needed data was set. * If it's not, we don't want to run the query, * so skip the rest of this section. */ if ( !isset($Data['LAST-SALE-PRICE']) ) { $DataProbs[] = "$Sym LAST-SALE-PRICE wasn't set"; } if ( !isset($Data['TRADE-DATETIME']) ) { $DataProbs[] = "$Sym TRADE-DATETIME wasn't set"; }
if ( count($DataProbs) ) { echo "\nData for $Sym had problems:\n"; echo implode("\n", $DataProbs) . "\n\n"; $DataProbs = array(); } else { // Construct a sample query string. $Query = 'UPDATE Quotes SET ' . "TradePrice={$Data['LAST-SALE-PRICE']}, " . "TradeTime='{$Data['TRADE-DATETIME']}' " . "WHERE CUSIP='{$Data['EQUITY-QUOTE:CUSIP']}'";
/* * In the real world, you could run the query now. But, * for the sake of this exercise, let's just look at it. */ echo "$Query\n"; } } }
// Loop through each ticker symbol. foreach ($Symbols as $Sym) {
/* * Grab the file and stick it into an array. * Next, check to see that you actually got the raw info. * Then, implode the raw info into one long string. * * If your data is already in string form, you don't need these steps. * * This one step requires PHP to be at version 4.3.0 or later. */ $Contents = @file_get_contents("$URI$Sym");
if (!$Contents) { $ParserProbs[] = "$URI$Sym\n Had problem opening file."; /* * Start the while loop over again, this time with the * next item in the $Symbols array. */ continue; }
// debug // echo "\n\n$URI$Sym\n"; // debug // echo "$Contents";
/* * Take care of characters that choke the parser. * While I don't think NASDAQ's data poses these problems, * it's good to keep them in mind. */
// Escape ampersands that aren't part of entities. $Contents = preg_replace('/&(?!\w{2,6};)/', '&', $Contents);
// Remove all non-visible characters except SP, TAB, LF and CR. $Contents = preg_replace('/[^\x20-\x7E\x09\x0A\x0D]/', "\n", $Contents);
/* * Clean out the Data array so it can be reused * to hold the data we parse from the file. */ $Data = array();
// Initialize the parser. $Parser = xml_parser_create('ISO-8859-1'); xml_set_element_handler($Parser, 'StartHandler', 'EndHandler'); xml_set_character_data_handler($Parser, 'CharacterHandler');
// Pass the content string to the parser. if ( !xml_parse($Parser, $Contents, TRUE) ) { $ParserProbs[] = "Had problem parsing data for $Sym:\n " . xml_error_string(xml_get_error_code($Parser)); } }
// Problems? if ( count($ParserProbs) ) { echo "\n" . implode("\n", $ParserProbs); }
?>
4 D How do I read XML data into the server using PHP?
You should not have to have a php script that parses the data back together
if you set the content type of the xml to "text/xml". This was
a bug in version 5,0,30,0 (the initial released version with the dev studio).
See link: http://www.macromedia.com/support/flash/ts/documents/xml_content_type.htm
It always sends a content type of "application/x-www-form-urlencoded".
But in version 5,0,42,0 (only through the webbrowser) of the player you
have a new XML member called contentType that I can set the type to "text/xml".
Now, when I do this $HTTP_RAW_POST_DATA will be set with my XML and not
chopped into nasty key value pairs.
And the second minor tidbit, for PHP developers: In version 4.0.7 there
will be a config directive to force $HTTP_RAW_POST_DATA to stay set no
matter what. For those of you interested, here's my code (and it seems
to work for me - heavily borrowed from macromedia example):
Flash:
on (release) {
// A. Construct a XML document with a LOGIN element
loginXML = new XML();
loginElement = loginXML.createElement("LOGIN");
loginElement.attributes.username = "MyDog";
loginElement.attributes.password = "barkbark";
loginXML.appendChild(loginElement);
// this only works in version 5,0,42,0 or greater (test in web browser)
loginXML.contentType = "text/xml";
// B. Construct a XML object to hold the server's reply
loginReplyXML = new XML();
loginReplyXML.onLoad = onLoginReply;
// C. Send the LOGIN element to the server,
//place the reply in loginReplyXML
loginXML.sendAndLoad("xmlreader.php", loginReplyXML);
}
xmlreader.php PHP CODE:
<?
// make sure you have the proper permissions to write to this file
$filename = "xmloutput.txt";
$fp = fopen( $filename,"w+");
fwrite ( $fp, "$HTTP_RAW_POST_DATA" );
fclose( $fp );
print '<?xml version="1.0"?><result>LOGIN OK</result>';
?>
and I end up with an xmloutput.txt file with this in it:
<login password="barkbark" username="MyDog"
/>
Another options is:
Anyway the kludge is to do some parsing in PHP before invoking the XML
engine. The faulty content-type is causing PHP to parse the data as if
it were url encoded ('attribute1=value1&attribute2=value2...') and
place it in $HTTP_POST_VARS instead of $HTTP_RAW_POST_DATA where the XML
engine expects it. It's trivial to set things right once you know what's
gone wrong:
$s = '';
while(list($key, $val) = each($HTTP_POST_VARS))
{
$s .= $key . '=' . $val;
}
$s = str_replace('_', ' ', stripslashes($s));
$HTTP_RAW_POST_DATA = $s;
Then just proceed as normal, invoking your XML parser of choice...
XML class
Availability
Flash Media Server 2.
Description
The XML class lets you load, parse, send, build, and manipulate XML document trees.
You must use the constructor new XML() to create an XML object before calling any method of the XML class.
An XML document is represented in Flash by the XML class. Each element of the hierarchical document is represented by an XMLNode object.
Property summary for the XML class
|
Property
|
Description
|
|
XML.attributes
|
An object that contains all the attributes of the specified XML object.
|
|
XML.childNodes
|
Read-only; an array of the specified XML object's children. Each element in the array is a reference to an XML object that represents a child node.
|
|
XML.contentType
|
The MIME type transmitted to the server.
|
|
XML.docTypeDecl
|
Sets and returns information about an XML document's DOCTYPE declaration.
|
|
XML.firstChild
|
Read-only; references the first child in the list for the specified node.
|
|
XML.ignoreWhite
|
When set to true, discards, during the parsing process, text nodes that contain only white space.
|
|
XML.lastChild
|
Read-only; references the last child in the list for the specified node.
|
|
XML.loaded
|
Checks whether the specified XML object has loaded.
|
|
XML.localName
|
Read-only; the local name portion of the XML node's name.
|
|
XML.namespaceURI
|
Read-only; if the XML node has a prefix, namespaceURI is the value of the xmlns declaration for that prefix (the URI), which is typically called the namespace URI.
|
|
XML.nextSibling
|
Read-only; references the next sibling in the parent node's child list.
|
|
XML.nodeName
|
The node name of an XML object.
|
|
XML.nodeType
|
Read-only; the type of the specified node (XML element or text node).
|
|
XML.nodeValue
|
The text of the specified node if the node is a text node.
|
|
XML.parentNode
|
Read-only; references the parent node of the specified node.
|
|
XML.prefix
|
Read-only; the prefix portion of the XML node name.
|
|
XML.previousSibling
|
Read-only; references the previous sibling in the parent node's child list.
|
|
XML.status
|
A numeric status code that indicates the success or failure of an XML document parsing operation.
|
|
XML.xmlDecl
|
Specifies information about a document's XML declaration.
|
Method summary for the XML class
Event handler summary for the XML class
|
Event handler
|
Description
|
|
XML.onData
|
Invoked when XML text has been completely downloaded from the server or when an error occurs downloading XML text from a server.
|
|
XML.onHTTPStatus
|
Invoked when Flash Media Server receives an HTTP status code from the server. This handler lets you capture and act on HTTP status codes.
|
|
XML.onLoad
|
Returns a Boolean value indicating whether the XML object was successfully loaded with XML.load() or XML.sendAndLoad().
|
Collections summary for the XML class
|
Method
|
Description
|
|
XML.attributes
|
Returns an associative array that contains all the attributes of the specified node.
|
|
XML.childNodes
|
Read-only; returns an array that contains references to the child nodes of the specified node.
|
h
Constructor for the XML class
Availability
Flash Media Server 2.
Usage
Parameters
source A string; the XML text parsed to create the new XML object.
Returns
A reference to an XML object.
Description
Constructor; creates a new XML object. You must use the constructor to create an XML object before you call any of the XML class methods.
|
NOTE |
|
Use the createElement() and createTextNode() methods to add elements and text nodes to an XML document tree.
|
Example
The following example creates a new, empty XML object:
The following example creates an XML object by parsing the XML text specified in the source parameter and populates the newly created XML object with the resulting XML document tree:
var other_xml = new XML("<state name=\"California\"><city>San Francisco</city></state>");
See also
XML.createElement(), XML.createTextNode()
XML.sendAndLoad()
Media Server 2.
Usage
my_xml.sendAndLoad(url, targetXMLobject)
Parameters
url A string; the destination URL for the specified XML object. If the SWF file issuing this call is running in a web browser, url must be in the same domain as the SWF file.
targetXMLobject An XML object created with the XML constructor method that will receive the return information from the server.
Returns
Nothing.
Description
Method; encodes the specified XML object into an XML document, sends it to the specified URL using the POST method, downloads the server's response, and loads it into the targetXMLobject object specified in the parameters. The server response loads the same as the response to the XML.load() method.
When sendAndLoad() is executed, the XML object property loaded is set to false. When the XML data finishes downloading, the loaded property is set to true if the data successfully loads and the onLoad event handler is invoked. The XML data is not parsed until it is completely downloaded. If the XML object previously contained any XML trees, they are discarded.
Example
The following example includes ActionScript for a simple e-commerce storefront application. The XML.sendAndLoad() method transmits an XML element that contains the user's name and password and uses an onLoad handler to process the reply from the server.
var login_str = "<login username=\""+username_txt.text+"\" password=\""+password_txt.text+"\" />"; var my_xml = new XML(login_str); var myLoginReply_xml = new XML(); myLoginReply_xml.ignoreWhite = true; myLoginReply_xml.onLoad = myOnLoad; my_xml.sendAndLoad("http://www.flash-mx.com/mm/login_xml.cfm", myLoginReply_xml); function myOnLoad(success) { if (success) { if ((myLoginReply_xml.firstChild.nodeName == "packet") && (myLoginReply_xml.firstChild.attributes.success == "true")) { gotoAndStop("loggedIn"); } else { gotoAndStop("loginFailed"); } } else { gotoAndStop("connectionFailed"); } }
Flash remoting for PHP: A responsive Client-Server Architecture for the Web
www.amfphp.org/
Introduction
Amfphp is an RPC toolkit for PHP. It allows seamless communication between Php and :
- Flash and Flex with Remoting
- JavaScript and Ajax with JSON
- XML clients with XML-RPC
Flash remoting for PHP: A responsive Client-Server Architecture for the Web
www.amfphp.org/
What's RPC?
RPC (Remote Procedure Call) is a way to communicate data between a client and a server. You call a method on a local object with various parameters, set a callback, and receive a result. You don't have to worry about how you're going to send and receive the data. The server and the client, say php and Flash, agree on a common way of describing method calls and complex data. The implementation details are abstracted away so that it looks as though you're calling a local method.
Amfphp lets you focus on features instead of implementation details. Testing remote services can be tricky, so amfphp has a built-in service browser which lets you test your services before you start writing the front end, and allows you to generate code for various clients. You can check it out here.
Flash remoting for PHP: A responsive Client-Server Architecture for the Web
www.amfphp.org/
What does the code look like in the example?
Several remote methods are defined in this service. Focusing on the getOrderList function, we have:
<?php
class pizzaService {
var $ordertable = "amfphp_orders"; // the orders table
var $pizzatable = "amfphp_pizzas"; // the pizzas table
/* mysql_connect and mysql_select_db are in the constructor */ function getOrderList ()
{
$sql = "SELECT o.order_id as orderid,
o.order_status as status,
o.order_name as name, p.pizza_id as pizzaid, p.pizza_details as details, p.pizza_quantity as quantity FROM $this->ordertable o, $this->pizzatable p WHERE o.order_id = p.order_id AND o.order_status=1 ORDER BY o.order_time";
return mysql_query($sql);
}
/* Other methods below */}
?>
The ActionScript code for getting the order list looks like the following:
import mx.remoting.*;
import mx.rpc.*;
var gatewayUrl:String = "http://amfphp.org/amfphp/gateway.php";
service = new Service(gatewayUrl, null, "pizzaService");
var pc:PendingCall = service.getOrderList();
pc.responder = new RelayResponder(this, "handleGetOrderList", null);
function handleGetOrderList(re:ResultEvent)
{
var rs:RecordSet = RecordSet(re.result);
for(var i = 0; i < rs.length; i++) {
var item = rs.getItemAt(i);
//item is an object with keys orderid, status, etc.
}
}
The resource returned by MySQL query is automatically converted to the corresponding ActionScript type, mx.remoting.RecordSet. You didn't have to loop through your query, you didn't have to explode URL-encoded strings on a pipe (|) separator, you didn't have to write code to generate XML, you didn't have to use xml.firstChild.firstChild.firstChild.childNodes[i].
What if we wanted to send an argument to the remote method? Then we would simply call service.getOrderList(firstArg). firstArg could be a string, a number, an array, or an object, and we would receive the corresponding types in php. As you can see, you don't have to worry about serializing and deserializing your data, instantiating your class, or making sure the method exists before calling this, as amfphp does all of this for you. You can grab the PHP source here and the Flash source in the ARP example apps.
|