I am using Inno Setup to create my installer for a desktop AIR app. I have my desktop app check my database for an app version number to let the user know if there is an update ready to install. I would like to have a pretty seamless update process for the user, so if they want to update, I would like for AIR to download the EXE and then run it.
1.) How do you download big files like an EXE from the web in AIR and do you need to place it someplace specific?
2.) Once the file is downloaded, how do I get AIR to execute the EXE?
Thanks!
public static function download(sourceURL:String, destinationPath:String, complete:Function = null, progress:Function = null, error:Function = null):void
{
var urlReq:URLRequest = new URLRequest(sourceURL);
var urlStream:URLStream = new URLStream();
var fileData:ByteArray = new ByteArray();
if(progress != null) urlStream.addEventListener(ProgressEvent.PROGRESS, progress);
if(error != null) urlStream.addEventListener(IOErrorEvent.IO_ERROR, error);
urlStream.addEventListener(Event.COMPLETE, loaded);
urlStream.load(urlReq);
function loaded(event:*):void
{
urlStream.readBytes(fileData, 0, urlStream.bytesAvailable);
writeFile();
}
function writeFile():void
{
var file:File = new File(destinationPath + sourceURL.slice(sourceURL.lastIndexOf("/"), sourceURL.length));
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeBytes(fileData, 0, fileData.length);
fileStream.close();
if(complete != null) complete();
}
}
var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
nativeProcessStartupInfo.executable = file;
process = new NativeProcess();
process.closeInput();
process.start(nativeProcessStartupInfo);
Related
Hi I'm trying to set up a Wifi Hotspot from Adobe Air with
var netsh:NativeProcessStartupInfo = new NativeProcessStartupInfo();
var file:File = File.applicationDirectory.resolvePath("C:/Windows/System32/netsh.exe");
netsh.executable = file;
var processArgs:Vector.<String> = new Vector.<String>();
processArgs.push("wlan set hostednetwork mode='allow' ssid='tests' key='tests123'");
netsh.arguments = processArgs;
process = new NativeProcess();
process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.start(netsh);
The application is starting, but doesn't like the arguments - throwing the error:
The following command was not found: "wlan set hostednetwork mode='allow' ssid='tests' key='tests123'".
Is there a better way of doing this? Is it even possible!?
I have tried without quotes - this was just the latest attempt
This works
var netsh:NativeProcessStartupInfo = new NativeProcessStartupInfo();
var file:File = File.applicationDirectory.resolvePath("C:\\Windows\\System32\\cmd.exe");
netsh.executable = file;
var processArgs:Vector.<String> = new Vector.<String>();
processArgs.push("/c netsh wlan set hostednetwork mode=allow ssid=test key=test1234");
netsh.arguments = processArgs;
process = new NativeProcess();
process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.start(netsh);
I've make an application with AIR and ActionScript 3 ( Using the IntelliJ IDE )
At start of the application, it loads some zip file and extracts their files to put them in the local storage. For futur use ( Or an utilisation if the user kills then re-opens the application )
It works correctly without problem the most of the time except that I have, sometimes, with a very low rate of appearence, a crash of the application during the download of those zip files.
The strange thing is these crash can occur directly in the emulator on my computer ( With a message that indicates an error in AIR ).
It is really difficult to check because these crashes may occur, something, like one time on forty launches and seems to never occur when trace() are with the debug mode.
That makes me think it's probably a problem with the speed of the loading, because all trace() slows down the launches and files loading.
Apparently, it's not a memory crash. I've tested and used the same code to load a really high number of same zip together and it doesn't crash.
I can't directly show the code, because it is implanted in a more complex framework.
The concerned code is something near to this :
private static function fileLoad( url:String ):void
{
var loader:URLLoader = new URLLoader();
var header:URLRequestHeader = new URLRequestHeader( "pragma" , "no-cache" );
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener( Event.COMPLETE, complete );
var request:URLRequest = new URLRequest( url );
request.requestHeaders.push( header );
request.method = URLRequestMethod.GET;
loader.load( request );
}
private static function complete( event:Event ):void
{
var file:File;
file = File.applicationStorageDirectory.resolvePath( "ZIP.zip" );
var bytes:ByteArray = event.target.data as ByteArray;
var stream:FileStream = new FileStream();
stream.open( file , FileMode.WRITE );
stream.writeBytes( bytes );
stream.close();
var loadZip:Function = function( event:FZipEvent ):void
{
var zip:FZipFile = event.file;
if ( zip.sizeUncompressed != 0 )
{
var fileData:Object = {};
var file:File = outputFile.resolvePath( zip.filename );
var stream:FileStream = new FileStream();
file.preventBackup = true;
stream.open( file, FileMode.WRITE );
stream.writeBytes( zip.content );
stream.close();
}
};
var inputFile:File = File.applicationStorageDirectory.resolvePath( "ZIP.zip" );
var outputFile:File = File.applicationStorageDirectory.resolvePath( "folderZip" );
var zipFileBytes:ByteArray = event.target.data as ByteArray;
stream = new FileStream();
stream.open( inputFile , FileMode.READ );
stream.readBytes( zipFileBytes );
stream.close();
var zip:FZip = new FZip();
zip.addEventListener( FZipEvent.FILE_LOADED , loadZip );
zip.load( new URLRequest( inputFile.nativePath ) );
}
I don't really expect a total solution but, if you have encountered a similar problem, I'll be thankfull to know what you've done to resolve it.
Thanks a lot
I have the following code in my AS3 Flash code that takes a screenshot within the swf using JPGEncoder and sends it to the url where i write it to a file in PHP.
I did run into the Google Chrome Pepperflash issue recently where the function just stops and the page fails to redirect. Nothing gets sent to save.php. By changing
var header:URLRequestHeader = new URLRequestHeader ("Content-type", "application/octet-stream");
to
var header:URLRequestHeader = new URLRequestHeader ("Content-type", "text/plain");
That seemed to do the trick. As of today though this works in internet Explorer but no longer in Chrome, Safari, Firefox. I saw that Adobe put out an update/patch to flash and flash player yesterday - could that have anything to do with it?
If i remove the following:
var header:URLRequestHeader = new URLRequestHeader ("Content-type", "text/plain");
jpgURLRequest.requestHeaders.push(header);
Then the page successfully redirects but $GLOBALS['HTTP_RAW_POST_DATA'] is then empty so no image file can be created.
Is there an alternative header i can put that will solve this?
My code is:
AS3:
function createJPG(m:MovieClip, q:Number, fileName:String) {
var jpgSource:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
jpgSource.draw(stage);
var jpgScreenshot: BitmapData = new BitmapData(362, 310);
jpgScreenshot.copyPixels(jpgSource, new Rectangle(288, 89, 362, 310), new Point(0, 0));
var jpgEncoder:JPGEncoder = new JPGEncoder(q);
var jpgStream:ByteArray = jpgEncoder.encode(jpgScreenshot);
var header:URLRequestHeader = new URLRequestHeader ("Content-type", "text/plain");
var jpgURLRequest:URLRequest = new URLRequest ("http://www.url.com/save.php");
jpgURLRequest.requestHeaders.push(header);
jpgURLRequest.method = URLRequestMethod.POST;
jpgURLRequest.data = jpgStream;
var jpgURLLoader:URLLoader = new URLLoader();
navigateToURL(jpgURLRequest, "_self");
}
save.php
$imagefile=''.$imageURL.'';
$fp = fopen($imagefile, 'wb');
fwrite($fp, $GLOBALS['HTTP_RAW_POST_DATA']);
fclose($fp);
header('Location: https://www.url.com/your-image.php');
Managed to get this working now with the following code. Liam was correct on the Flash Player issue. Working now by separating saving the image and navigating to the url into 2 differents function:
function createJPG(m:MovieClip, q:Number, fileName:String) {
var jpgSource:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
jpgSource.draw(stage);
var jpgScreenshot: BitmapData = new BitmapData(362, 310);
jpgScreenshot.copyPixels(jpgSource, new Rectangle(288, 89, 362, 310), new Point(0, 0));
var jpgEncoder:JPGEncoder = new JPGEncoder(q);
var jpgStream:ByteArray = jpgEncoder.encode(jpgScreenshot);
var urlLoader:URLLoader = new URLLoader();
var header:URLRequestHeader = new URLRequestHeader("Content-type", "application/octet-stream");
var saveJPG:URLRequest = new URLRequest('http://www.url.com/save_image.php?name=filename';
saveJPG.requestHeaders.push(header);
saveJPG.method = URLRequestMethod.POST;
saveJPG.data = jpgStream;
urlLoader.addEventListener(Event.COMPLETE, goToCheckout);
urlLoader.load(saveJPG);
}
function goToCheckout(e:Event):void{
var url:String = 'http://www.url.com/show_image.php';
var request:URLRequest = new URLRequest(url);
try {
navigateToURL(request, '_self');
} catch (e:Error) {
trace("Error occurred!:");
}
}
save_image.php:
if(isset($GLOBALS["HTTP_RAW_POST_DATA"])){
$jpg = $GLOBALS["HTTP_RAW_POST_DATA"];
$path = "";
$id = $_GET["name"];
$file = $id;
file_put_contents($path.$file, $jpg);
echo "complete";
} else{
// error
}
Flash Player version 13.0.0.214 introduces some key security fixes. Unfortunately it also breaks navigateToUrl() by forbidding the changing of any headers in the request given to navigateToUrl(). This breaks POST requests that need to pass in security/session token headers, or to even change the Content-Type, e.g. to text/xml, etc. as you have done in your example.
Right now, as much as it sucks, our best known workaround is to downgrade clients to 13.0.0.206
Adobe proposes using external interface call as described here: https://forums.adobe.com/message/6396080
it uses JavaScript to replace the POST method
The solution proposed by odd_duck seems simpler though - would be great to see the php code as well.
I try to download an external SWF and run it within the AIR security sandbox.
this is the code of the AIR application:
public class Downloader extends Sprite
{
private static const REMOTE_FILE:URLRequest = new URLRequest("http://myserver.com/downloadable.swf");
private var _main:NativeWindow;
public function Downloader()
{
var loader:URLLoader = new URLLoader(REMOTE_FILE);
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, downloadComplete);
}
private function downloadComplete(e:Event):void{
var ba:ByteArray = e.target.data;
var stream:FileStream = new FileStream();
var file:File = File.applicationStorageDirectory.resolvePath("downloadable.swf");
stream.open(file, FileMode.WRITE);
stream.writeBytes(ba);
stream.close();
loadAndRunSwf();
}
private function loadAndRunSwf(){
this._main = new NativeWindow();
this._main.width = 1024;
this._main.height = 768;
////obsolete?
//var context:LoaderContext = new LoaderContext();
//context.allowLoadBytesCodeExecution = true;
//context.applicationDomain = ApplicationDomain.currentDomain;
var file:File = File.applicationStorageDirectory.resolvePath("downloadable.swf");
var loader:Loader = new Loader();
loader.load(new URLRequest(file.url)/*,context*/);
this._main.stage.addChild(loader);
this._main.activate();
}
}
Code of the downloadable.swf:
public class Downloadable extends Sprite
{
private var _btn:Button = new Button();
private var _baseFolder:File = new File("app-storage:/");
public function downloadable_test()
{
this.addChild(_btn);
_btn.label = "access Harddisk";
...
}
}
so now, If I run Downloader, it will download the swf and try to run it, but i'll get an exception in Downloadable on the line
private var _baseFolder:File = new File("app-storage:/");
the error:
SecurityError: file
at runtime::SecurityManager$/checkPrivilegeForCaller()
So - what Do I need to do to prevent such security errors? I want my remote SWF to be treated as native code running in the same security sandbox as the AIR code.
I'm not sure about Android, but for the regular web player you would need to specify SecurityDomain.currentDomain for the securityDomain of the Loader's context for the loaded code to be considered equal to the loader in terms of permissions. Also note that for reasons impossible to explain, if you use SecurityDomain when loading from the file system on PC Flash Player will complain.
However complicated, the Flash Player security is often a security by obscurity... So, if it doesn't work the way you coded it, try Loader.loadBytes() "workaround".
function loadAndRunSwf()
{
var context:LoaderContext=new LoaderContext(false);
context.allowCodeImport=true;
var ba:ByteArray=new ByteArray();
var file:File = File.applicationStorageDirectory.resolvePath("downloadable.swf");
var fileStream:FileStream=new FileStream();
var loader:Loader = new Loader();
fileStream.open(file,"read");
fileStream.readBytes(ba);
ba.position=0;
loader.loadBytes(ba,context);
this._main = new NativeWindow();
this._main.width = 1024;
this._main.height = 768;
this._main.stage.addChild(loader);
this._main.activate();
}
I am trying to save a Sprite object as a file on the device I'm working on and it seems to work. the problem I'm having is reading the saved file back and placing it back on stage as a sprite. Below is the code I have so far, could someone tell me what it is I'm doing wrong? I have a suspicion that the saved isn't what I expect it to be since the file sizes have been under a kilobyte.
public function save_album(e:Event):void
{
var outFile:File = File.documentsDirectory; // dest folder is desktop
outFile = outFile.resolvePath("canvas3.bin");
var fs:FileStream = new FileStream();
var bytes:ByteArray = new ByteArray();
//trace (File.documentsDirectory.url + "/canvas2.bin");
fs.open(outFile, FileMode.WRITE);
bytes.writeObject(graffitiContainer) //graffitiContainer is a Sprite
bytes.position = 0;
fs.writeBytes(bytes, 0, bytes.length);
fs.close();
}
public function open_album(e:Event):void
{
var inBytes:ByteArray = new ByteArray();
var inFile:File = File.documentsDirectory;
inFile = inFile.resolvePath("canvas3.bin"); // name of file to read
var inStream:FileStream = new FileStream();
inStream.open(inFile, FileMode.READ);
inStream.readBytes(inBytes, 0, inBytes.length);
inStream.close();
inBytes.position = 0;
ui.removeChild(graffitiContainer);
var obj:Sprite = inBytes.readObject() as Sprite; //returns a null
graffitiContainer = obj;
ui = new UIComponent();
graffitiContainer.x = 0;
graffitiContainer.y = 100;
ui.addChild(graffitiContainer);
}
Not fully sure I understand what you're trying to accomplish; however, this implementation doesn't do what you're thinking - writeObject could only serialize general public properties, and not the graphics member.
You could render it to a Bitmap.
Saw a blog post about this:
http://jacwright.com/201/serializing-display-objects/