Tagged: zinc

Easy Multipart file uploads / POSTs using URLStream or AS3HttpClientLib

When working on a cross runtime Zinc/AIR method method of posting files to a remote server, I came across Mike Stead’s URLFileVariable and URLRequestBuilder classes which makes the process of uploading multiple files (or ByteArray’s of data) via an HTTP POST much easier. Click here get his code

The basic concept is this: assuming you have one or more ByteArray’s of file data and the filenames to go with them, you prepare one or more URLFileVariable’s. The URLFIleVariable’s are added to the URLVariables as follows:

// create the standard "URLVariables"
var vars:URLVariables = new URLVariables(); 
// lets set a ByteArray of JPG data with the filename "myFile.jpg" against the POST variable "file1"
vars.file1 = new URLFileVariable(myByteArray, "myFIle.jpg");

Now at this point, as in my case, I am passing these URLVariables to either an AIR implementation of an HTTP client (using URLStream) or a Zinc implementation (using AS3HttpClientLib). This was all described here in another post.

In my AIR implementation of an HTTP client abstraction I use an URLStream and the code that deals with the URLVariables works something like the following.


/* Standard URLStream code that interacts with URLVariables
which contain Mike Stead's URLFIleVariables and uses his URLRequestBuilder */

var stream:URLStream = new URLStream();

// [ REGISTER for your stream listeners here...

/* use Mike Stead's URLRequestBuilder to properly build the 
URLRequest and encode it to contain all the multi-part data and filenames in the "vars" created above */
var req:URLRequest = new URLRequestBuilder(vars).build();

// ensure a POST because URLRequestBuilder only specifies POST if the variables contains files.
req.method = URLRequestMethod.POST; 

// define some URL to post to
req.url = "http://my.something.com.xyz/url_to_post_to.xyz";

// fire off the POST
stream.load(req);

// as the load completes, your event handlers for the stream should take over

Now on the other hand, if you happen to be using AS3HttpClientLib (as described in one of my other posts) you will be coding the equivalent of the URLStream code above, however using AS3HttpClientLib as follows. Here we do not use the URLRequestBuilder but simply manually retrieve the appropriate information from Mike’s URLFileVariable instance.

....

// our AS3HttpClientLib HttpClient
var client:HttpClient = new HttpClient();

...
// [Remember to setup your "client" event hooks as described in the AS3HttpClientLib docs here]

// create a new POST request
var postReq:HttpRequest = new Post();

// lets create an Array to hold all the different "parts" of this multipart post
var parts:Array = new Array();

/* lets push the URLFileVariable we created earlier into the parts Array 
according to how AS3HttpClientLib expects it, as a Part object. Here
we are simply creating a new Part and populating it with the data
that we defined up above when we created the URLFileVariable */

parts.push(new Part("file1",vars.file1.data, "application/octet-stream", [{name:"filename",value:vars.file1.name}]));

// create a multipart that points to the "parts" Array
var mp:Multipart = new Multipart(parts);
postReq.setMultipart(mp);
					
// create a URI
var uri:URI = new URI("http://my.something.com.xyz/url_to_post_to.xyz");	

// fire off the request
client.request(uri,postReq);

Thanks again to Mike Stead’s UrlFileVariable and URLRequestBuilder classes for helping me make an generic HTTP multipart post process easier and I hope my post will give those reading some help along the way on implementing their own.

Advanced HTTP operations in Flex outside of AIR

I am currently pretty deep into a Flex/AS3 RIA desktop app project whereby I have several advanced needs. Such as to download partial files to the desktop (byte-range requests), execute HEAD requests to get remote file sizes, execute multipart POSTs, talk to some REST apis, and finally be able to read any write HTTP headers….. outside of AIR? Good luck with URLStream, URLRequest, URLRequestMethod and URLRequestHeader

Due to security sandbox restrictions, unless your application is running within Adobe AIR you are restricted to simple GETs and POSTs. You are also NOT allowed to touch the following headers:

Accept-Charset, Accept-Encoding, Accept-Ranges, Age, Allow, Allowed, Authorization, Charge-To, Connect, Connection, Content-Length, Content-Location, Content-Range, Cookie, Date, Delete, ETag, Expect, Get, Head, Host, Keep-Alive, Last-Modified, Location, Max-Forwards, Options, Origin, Post, Proxy-Authenticate, Proxy-Authorization, Proxy-Connection, Public, Put, Range, Referer, Request-Range, Retry-After, Server, TE, Trace, Trailer, Transfer-Encoding, Upgrade, URI, User-Agent, Vary, Via, Warning, WWW-Authenticate, x-flash-version.

That’s basically all of the ones I needed to touch…..so what to do? In my case I was developing some file management functionality that runs in both Adobe AIR as well as MDM Zinc. Amongst other protocols such as FTP, I needed to be able to pull file updates over HTTP as well (+authentication, HEAD checks etc). So I had to create an generic IHttpClient interface abstraction which then allowed me to implement runtime specific clients. So for the AIR side of things I was good to go by using all of Adobe’s URL* classes right out of the box and they work great for the AirHttpClient implementation of my interface. However for my Zinc client I was still stuck, until I ran across a fantastic little HTTP library out there that provides a custom HTTP library built on top of the Flex socket stack.

The AS3 library that ended up being used in my ZincHttpClient was: as3httpclientlib. as3httpclientlib solved all of my problems when running outside of the AIR environment. With as3httpclientlib you can do all of the following and more:

* GET, HEAD, PUT, POST, DELETE
* multipart/form-data (PUT and POST)
* HTTPS support using AS3Crypto TLS
* Post with application/x-www-form-urlencoded
* Reading chunked (Transfer-Encoding)

If you have the need to access a bit more advanced HTTP functionality and need to do it outside of the AIR runtime. I HIGHLY recommend as3httpclientlib. I have put this library through the works doing byte range requests, HEAD requests, header manipulation and downloading and posting all sorts of file sizes, the library works great. My only note is that it is a tad slower than URLStream based HTTP downloads under Adobe AIR, regardless, this little HTTP library is worth it. Kudos to the AS3HttpClientLib team!

Local IO write performance in Zinc improved

Looks like the folks over at MDM have improved their local IO write support (windows only) with the release of v3.0.10 of MDM Zinc. This is probably due to Adobe AIR and people’s complaints about the lack of better support for writing ByteArray data to disk in Zinc. Note that this new feature is only available when you are running on a PC
(mac/linux still have to use the old HEX conversion method)

In a previous post I wrote about the slow performance of writing binary data in Zinc. However now it is definitely faster, however still not as fast as writing under Adobe AIR. Regardless, it looks like MDM has listened to their users and made a step to improve things and that is great.

Here is an example of how to write a ByteArray to disk now under 3.0.10. Note this is faster than the old HEX conversion method and is much faster and less code:

var data:ByteArray = // some byte array of your data
mdm.FileSystem.BinaryFile.setDataBA(data);
mdm.FileSystem.BinaryFile.writeDataBA("c:\some\path\to\file");

Kudos to the folks at MDM for adding this new feature.

Local disk IO write performance in MDM Zinc

UPDATE: MDM Zinc’s recent update of 3.0.10 added a new method for writing binary data from a ByteArray directly (windows only), please read this post for the latest info. NOTE that the below is still relevant for other platforms (Mac/Linux)

I have recently been working on local disk IO in MDM Zinc using mdmScript as well as in Adobe AIR, and wow are there quite some differences. Mainly and most notably write performance….. which is very slow in the Zinc runtime.

mdmScript appears to have mainly been developed for simple Flash apps that do basic IO centered around reading and writing text files vs. binary files (images, other large files etc). This is particularly evident when you need to write binary files.

Reading files is not a problem and it is not specific to mdmScript as you can just use standard AS3 such as in the simple example below when you are executing within the Zinc runtime. NOTE, reading in Adobe AIR is fairly similar, however you use the FileStream class. (and AIR is faster)


private var stream:URLStream;

function someReadFunction():void {
   // setup a url request for a local file
   var fileURL:URLRequest = new URLRequest("c:\somePath\to\my\file.jpg");

   // create and URLStream
   stream = new URLStream();

   // register for the complete event
   stream.addEventListener(Event.COMPLETE, handleReadComplete);

   // REMEMBER to also register for IOErrorEvent.IO_ERROR, 
   // and ProgressEvent.PROGRESS events! not here for brevity
   ...

   // fire the load of the file
   stream.load(fileURL);
}

// this method is called when the stream is finished loading
// your file 
private function handleReadComplete(e:Event):void {
   var data:ByteArray = new ByteArray();
   stream.readBytes(data,0,0);
   stream.close();
    
   // remove your event listeners to prevent memory leaks
   stream.removeEventListener(Event.COMPLETE,handleReadComplete);

  // do something with the data you just loaded
}

Writing binary files when running in Zinc is a different matter. You have to convert each byte to a HEX string and then call mdmScript’s mdm.FileSystem.BinaryFile.setData(hexString:String) method. This is painfully slow. Here is a basic sample:

var data:ByteArray = // some byte array of your data;

var hex:String = “”;
var byte:String;
var byteCount:int = 0; // total bytes converted, (start @ zero)
while (byteCount < data.length) { try { byte = data.readUnsignedByte().toString(16); hex += (byte.length == 1) ? "0" + byte : byte; byteCount++ } catch (e:EOFError) { break; // we are done } } // set the converted HEX data mdm.FileSystem.BinaryFile.setData(hex); // tell mdmScript to write the data to a file mdm.FileSystem.BinaryFile.writeData("c:\path\to\somefile"); [/sourcecode] Again this is just super slow once you start writing larger files... however until MDM adds better support for writing binary data, we have to live with this. In Adobe AIR to write files you use the FileStream class and it is orders of magnitude faster than writing with mdmScript under Zinc.

How to detect if your SWF is executing within the MDM Zinc runtime

Been poking around with writing a runtime abstraction layer for Flex/AS3 apps. This would provide support for writing Flex/AS3 desktop app code once and deploy it as an AIR app or any other swf2exe like product such as Zinc. (or FP on the web, -minus the IO features) So how do you detect which runtime you are executing in? If you are checking against Zinc you can do the following:

function isInZinc():Boolean {
	try{
		try {
			// "this" being your main mxml app
			mdm.Application.init(this as Sprite);

		} catch(e:Error) {}    



		// only this prop is available in all OS's

		if (mdm.System.localTime != null &&
 mdm.System.localTime.length > 0) {
    
			return true
		}
	} catch(e:Error) {
		return false;
	}
	return false;
}