Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>OK. This definitely seems to be a Mac OSX issue. For some reason, the HTTP headers created by File.upload() method in OSX cause the external server to crash with a 301 Move Permanently. This DOES NOT happen in Windows. Of course, anything other than a HTTP 2xx response will result in an I/O Error as far as the AIR compiler concerned. Therefore, in order to get around the file upload problem, we have to manually create the HTTP headers responsible for the upload <em>per se</em>. I would suggest not using <code>File.upload()</code> (or <code>FileReference.upload()</code> for that matter) until this issue is resolved. </p> <p>In order to do this, the file that we are uploading must be converted into a ByteArray. Therefore I would only use File/FileReference for capturing the file. For the actual upload, the file must be converted to <code>ByteArray</code>.</p> <p>In my example above, it's very simple:</p> <pre><code>import flash.net.* import flash.utils.ByteArray; var xml:XML = new XML(&lt;items&gt;&lt;item&gt;one&lt;/item&gt;&lt;item&gt;two&lt;/item&gt;&lt;/items&gt;); var data:ByteArray = new ByteArray(); data.writeUTF(xml.toXMLString()); </code></pre> <p>Now as far creating the manual HTTP headers, credit goes to Jonathan Marston (<a href="http://marstonstudio.com/2007/10/19/how-to-take-a-snapshot-of-a-flash-movie-and-automatically-upload-the-jpg-to-a-server-in-three-easy-steps/" rel="nofollow">http://marstonstudio.com/2007/10/19/how-to-take-a-snapshot-of-a-flash-movie-and-automatically-upload-the-jpg-to-a-server-in-three-easy-steps/</a>) and his beautiful <code>UploadPostHelper</code> class:</p> <pre><code>package { import flash.events.*; import flash.net.*; import flash.utils.ByteArray; import flash.utils.Endian; /** * Take a fileName, byteArray, and parameters object as input and return ByteArray post data suitable for a UrlRequest as output * * @see http://marstonstudio.com/?p=36 * @see http://www.w3.org/TR/html4/interact/forms.html * @see http://www.jooce.com/blog/?p=143 * @see http://www.jooce.com/blog/wp%2Dcontent/uploads/2007/06/uploadFile.txt * @see http://blog.je2050.de/2006/05/01/save-bytearray-to-file-with-php/ * * @author Jonathan Marston * @version 2007.08.19 * * This work is licensed under a Creative Commons Attribution NonCommercial ShareAlike 3.0 License. * @see http://creativecommons.org/licenses/by-nc-sa/3.0/ * */ public class UploadPostHelper { /** * Boundary used to break up different parts of the http POST body */ private static var _boundary:String = ""; /** * Get the boundary for the post. * Must be passed as part of the contentType of the UrlRequest */ public static function getBoundary():String { if(_boundary.length == 0) { for (var i:int = 0; i &lt; 0x20; i++ ) { _boundary += String.fromCharCode( int( 97 + Math.random() * 25 ) ); } } return _boundary; } /** * Create post data to send in a UrlRequest */ public static function getPostData(fileName:String, byteArray:ByteArray, parameters:Object = null):ByteArray { var i: int; var bytes:String; var postData:ByteArray = new ByteArray(); postData.endian = Endian.BIG_ENDIAN; //add Filename to parameters if(parameters == null) { parameters = new Object(); } parameters.Filename = fileName; //add parameters to postData for(var name:String in parameters) { postData = BOUNDARY(postData); postData = LINEBREAK(postData); bytes = 'Content-Disposition: form-data; name="' + name + '"'; for ( i = 0; i &lt; bytes.length; i++ ) { postData.writeByte( bytes.charCodeAt(i) ); } postData = LINEBREAK(postData); postData = LINEBREAK(postData); postData.writeUTFBytes(parameters[name]); postData = LINEBREAK(postData); } //add Filedata to postData postData = BOUNDARY(postData); postData = LINEBREAK(postData); bytes = 'Content-Disposition: form-data; name="Filedata"; filename="'; for ( i = 0; i &lt; bytes.length; i++ ) { postData.writeByte( bytes.charCodeAt(i) ); } postData.writeUTFBytes(fileName); postData = QUOTATIONMARK(postData); postData = LINEBREAK(postData); bytes = 'Content-Type: application/octet-stream'; for ( i = 0; i &lt; bytes.length; i++ ) { postData.writeByte( bytes.charCodeAt(i) ); } postData = LINEBREAK(postData); postData = LINEBREAK(postData); postData.writeBytes(byteArray, 0, byteArray.length); postData = LINEBREAK(postData); //add upload filed to postData postData = LINEBREAK(postData); postData = BOUNDARY(postData); postData = LINEBREAK(postData); bytes = 'Content-Disposition: form-data; name="Upload"'; for ( i = 0; i &lt; bytes.length; i++ ) { postData.writeByte( bytes.charCodeAt(i) ); } postData = LINEBREAK(postData); postData = LINEBREAK(postData); bytes = 'Submit Query'; for ( i = 0; i &lt; bytes.length; i++ ) { postData.writeByte( bytes.charCodeAt(i) ); } postData = LINEBREAK(postData); //closing boundary postData = BOUNDARY(postData); postData = DOUBLEDASH(postData); return postData; } /** * Add a boundary to the PostData with leading doubledash */ private static function BOUNDARY(p:ByteArray):ByteArray { var l:int = UploadPostHelper.getBoundary().length; p = DOUBLEDASH(p); for (var i:int = 0; i &lt; l; i++ ) { p.writeByte( _boundary.charCodeAt( i ) ); } return p; } /** * Add one linebreak */ private static function LINEBREAK(p:ByteArray):ByteArray { p.writeShort(0x0d0a); return p; } /** * Add quotation mark */ private static function QUOTATIONMARK(p:ByteArray):ByteArray { p.writeByte(0x22); return p; } /** * Add Double Dash */ private static function DOUBLEDASH(p:ByteArray):ByteArray { p.writeShort(0x2d2d); return p; } } } </code></pre> <p>Now, we utilize the class to create our HTTP header using good old <code>URLRequest</code>:</p> <pre><code>var req:URLRequest = new URLRequest(); req.url = //server-side url (.php) req.contentType = "multipart/form-data; boundary=" + UploadPostHelper.getBoundary(); req.method = URLRequestMethod.POST; //Be sure to place the actual file name with its extension as the file name. Set the second argument as the ByteArray created earlier. Any other parameters (name-value pairs) can be added via an optional third parameter (see class above) req.data = UploadPostHelper.getPostData("test.xml", data); req.requestHeaders.push(new URLRequestHeader("Cache-Control", "no-cache")); </code></pre> <p>Now instead of using <code>File.upload()</code> to complete the request, just use <code>URLLoader</code>:</p> <pre><code>var loader:URLLoader = new URLLoader(); //We have to load the data as binary as well loader.dataFormat = URLLoaderDataFormat.BINARY; loader.addEventListener(Event.COMPLETE, complete); loader.addEventListener(IOErrorEvent.IO_ERROR, ioerror); loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, secerror); loader.load(req); //Event handlers function complete(e:Event):void { var ba:ByteArray = e.target.data; //Returns the XML data trace(ba.readUTF()); } function ioerror(e:IOErrorEvent):void {} function secerror(e:SecurityErrorEvent):void {} </code></pre> <p>And all should work nicely. As far as the server-side is concerned, the name of the file is defined by the class above as <code>Filedata</code>. A simple PHP script to capture the file would look something like this:</p> <pre><code>&lt;?php echo file_get_contents($_FILES['Filedata']['tmp_name']); var_dump($_FILES['Filedata']); /* array(1) { ["Filedata"]=&gt;array(5) { ["name"]=&gt;string(8) "test.xml" ["type"]=&gt;string(24) "application/octet-stream" ["tmp_name"]=&gt;string(38) "root/tmp/php2jk8yk" ["error"]=&gt;int(0) ["size"]=&gt;int(58) } } */ ?&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload