how to download files to a default dir without poping an option window in Flex/Air? - apache-flex

I'm writing a app with Flex/air,and i need a function that downloading files to the default dir without a pop-up window.i tried to use ftp instead of http but found it's not supported by air.how can i solve this problem?

This should be possible in AIR. I don't know if there's a direct API approach, but you should be able to load the bytes into memory and then flush them into a file. In pseudo-pseudo-code using File and FileStream:
// get the bytes
var loader:URLLoader = new URLLoader();
loader.load("http://www.stackoverflow.com");
...
var bytes:ByteArray = loader.data;
// get the file in the correct location
var f:File = File.documentsDirectory.resolvePath("myfile.txt");
// write the file
var fs:FileStream = new FileStream(f, FileMode.WRITE);
fs.writeBytes(bytes);
fs.close();
There are a few examples out there to look at.
None of this is possible in Flash Player because of the security limits #viatropos suggests.

I don't think you can do that as it's a Security violation. The user must be prompted with a download window so they can choose the directory. I think the thinking goes "if you don't prompt the user, then a Flash/Flex app could download a bunch of junk to their computer without their permission", and that would make adobe look bad :/. I wish you could though.
You can, however, download it using a server side script without asking the user for permission. I do that with ruby to accomplish what you're describing.
Hope that helps,
Lance

You can but only air version because you have to use FileStream
var ff:File = File.desktopDirectory.resolvePath("sample.dwg");
var u:String = obj_l.d1.selectedItem.data + "" + obj_l.d2.selectedItem.label;
var ll3:URLLoader = new URLLoader ();
u = GETURL() + u;
rr.url = u;
//ff.download(rr);
ll3.dataFormat = URLLoaderDataFormat.BINARY;
ll3.load(rr);
ll3.addEventListener(Event.COMPLETE , function (e:Event):void {
var stream:FileStream = new FileStream ();
stream.open(ff,FileMode.WRITE);
stream.writeBytes(ll3.data);
});

Related

Is it possible to load a local file without having to ask the user to browse to it first in an AIR Application?

I'm writing a small application for myself and instead of using a database I'd like to just use Excel to store the small amount of data I have on my local file system. I want to be able to load that data without having to use the typical FileReference browse() method as it would just be annoying to do every time I use the application.
The code below seems to find the file fine as the file.exists method below and most of the other attributes seem to be correct but the file.data is always null.
I'm guessing this is a security issue and that's why I'm running into this problem but I thought I'd ask and see if there is in fact a way around this problem.
var file:File = new File(fullPath + "\\" + currentFolder + ".txt");
if(file.exists) {
var byteArray:ByteArray = file.data;
}
If you want to read the content of a file, use the following code:
var stream:FileStream = new FileStream();
stream.open("some path here", FileMode.READ);
var fileData:String = stream.readUTFBytes(stream.bytesAvailable);
trace(fileData);
The data property is inherited from FileReference class and it will be populated only after a load call (see this link).
You're close, you just need to combine that with a FileStream object
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.READ);
var str:String = fileStream.readMultiByte(file.size, File.systemCharset);
trace(str);
more info here

Flex 3 & Air 2: Automatically detect when files on a directory are updated

A crazy idea just dropped from the sky and hit me in the head xD. I was wondering if it is possible to make and App capable of listening when the user "adds" new files to a directory.
Example:
The User opens up our Application.
The user adds new files on the desktop (using the Microsoft Explorer).
Our application automatically detects that new files have been added and executes a function or whatever.
Sound interesting right?
Maybe, this could be done using a programming language like Visual Basic and open the executable with the NativeProcess api and listen for an stdOut event... (:
Anyone got and idea to share with us? :)
Thanks
Lombardi
AIR can handle this natively...
the FileSystemList class fires an event directoryChange whenever a file in the watched directory changes.
You can even use it to watch for drives being mounted (I think Christian Cantrell showed that one off)
Ok, I think I'm getting closer, check out this solution! :)
private var CheckDelay:Timer = new Timer(5000, 0);
private function InitApp():void
{
CheckDelay.addEventListener(TimerEvent.Timer, CheckForNewFiles, false, 0, true);
CheckDelay.start();
}
private function CheckForNewFiles(event:TimerEvent):void
{
var FS:FileStream = new FileStream();
var Buffer:File = File.applicationStorageDirectory.resolvePath("FilesBuffer.cmd");
FS.open(Buffer, FileMode.Write);
FS.writeUTFBytes("cd " + File.desktopDirectory.nativePath + "\r\n" +
"dir /on /b > " + File.applicationStorageDirectory.resolvePath("FileList.txt").nativePath);
FS.close();
var Process:NativeProcess = new NativeProcess();
var NPI:NativeProcessStartupInfo = NativeProcessStartupInfo(); // What a large name! xD
NPI.executable = Buffer;
Process.start(NPI);
Process..addEventListener(NativeProcessExitEvent.EXIT, ReadFileList, false, 0, true);
}
private function ReadFileList(event:Event):void
{
var FS:FileStream = new FileStream();
var Buffer:File = File.applicationStorageDirectory.resolvePath("FilesBuffer.cmd");
FS.open(Buffer, FileMode.Read);
var FileData:String = FS.readUTFBytes(FS.bytesAvailable);
FS.close();
var FileArray:Array = FileData.split("\r\n");
var TempArray:ArrayCollection = new ArrayColletion();
var TempFile:File;
for(var i:int = 0;i<FileArray.length;i++){
TempFile = new File(FileArray[i]);
TempArray.addItem(TempFile);
}
}
At the end we got an Array (TempArray) that we could use on a datagrid (for example) with colums like: "extension, File Name, FilePath, etc.."
The files are updated every 5 seconds.
And, why we use all that code instead of a simple "File.getDirectoryListing()"? Because we are updating our application every 5 seconds, if why use getDirectoryListing(), our application will take much more RAM and also, the cmd command is much faster... :)
If you have a better idea, please share it with us! Thank you! :D
1 excellent solution for Windows: use Visual Studio, build the .net app found here http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx
In Adobe AIR use the native process to listen for change events dispatched by .net

Downloading data posted to server as a file from Flex

This should be trivial, and I'm pretty sure I did it once before.
I'm trying to post data up to a server and have it bounced back to me as a file download, prompting the native browser file download box. I know the server part works just fine becasue I can post from a demo web form, but when I run the following Flex 3 code, I can't even get the request to fire.
var fileRef:FileReference = new FileReference();
private function saveXmlAsFile(event:MouseEvent):void
{
var fileRequest:URLRequest = new URLRequest();
fileRequest.method = URLRequestMethod.POST;
fileRequest.url = "http://foo.com/dataBounce";
var urlVariables:URLVariables = new URLVariables();
urlVariables.content = "Test content to return" ;
// fileRequest.contentType = "application/x-www-form-urlencoded ";
urlVariables.fileName = "test.xml";
fileRef.addEventListener(SecurityEvent.ALL, onSecurityError);
fileRef.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError2);
fileRef.addEventListener(IOErrorEvent.NETWORK_ERROR, onNetworkError);
fileRef.addEventListener(Event.COMPLETE, onComplete);
try
{
fileRef.download(fileRequest, "test.xml");
}catch(error:Error) {
model.logger.error("unable to download file");
}
}
Note, when the call to fileRef.download is called, I can't see any request being made across the network using the traditional Firebug or HTTPWatch browser tools.
EDIT: I should add that this is for < Flash Player 10, so I can't use the newer direct save as file functionality.
Any suggestions? Thanks.
You need to add fileRef.upload to trigger the upload.
Also I would move the download statement to the onComplete so the file isn't requested before it's been uploaded.
Your explanation is pretty clear, but when I look at your code, I'm feel like I'm missing something.
The code looks like you're trying to do half of the upload part and half of the download part.
I think the code you currently have posted would work to trigger a download if you set the .method value to GET. I believe you will also need to include the filename as part of the .url property.
However, to post something and then trigger a download of it, you need two separate operations - the operation to post the data and then an operation to download it, which should probably be called from the upload operation's onComplete handler.
OK, I believe I figured out one of the things that's going on.
When you don't set the URLRequest.data property, it defaults the request method to "GET".
So, the working code looks like, with the data set to the posted URL variables:
private var fileRef:FileReference;
private function saveRawHierarchy(event:MouseEvent):void
{
var fileRequest:URLRequest = new URLRequest();
fileRequest.method = URLRequestMethod.POST;
fileRequest.url = "http://foo/bounceback";
var urlVariables:URLVariables = new URLVariables();
urlVariables.content = "CONTENT HERE";
urlVariables.fileName = "newFileName.xml";
fileRequest.data = urlVariables;
fileRef = new FileReference();
fileRef.addEventListener(Event.COMPLETE, onComplete);
try
{
fileRef.download(fileRequest, "appHierarchies.xml");
}catch(error:Error) {
model.logger.error("unable to download file");
}
}
Not sure what was wrong about the request not being made before, though.

How can you save out a String into a file in AS3?

I have a string the user has typed and I want to save it into a file on the users harddrive. Can you do that? And if so, how?
Yes you can, with FileReference.
This is basically how it's done:
var bytes:ByteArray = new ByteArray();
var fileRef:FileReference=new FileReference();
fileRef.save("fileContent", "fileName");
Doesn't look too hard, does it?
And here's a video-tutorial on it too:
http://www.gotoandlearn.com/play?id=76
And the documentation:
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/
Hope that helps.
Since I had a function to output bytes to a file (because I was doing something with bitmaps), I reused it to output a string as well, like this:
var filename:String = "/Users/me/path/to/file.txt";
var byteArray:ByteArray = new ByteArray();
byteArray.writeUTFBytes(someString);
outFile(filename, byteArray);
private static function outFile(fileName:String, data:ByteArray):void {
var outFile:File = File.desktopDirectory; // dest folder is desktop
outFile = outFile.resolvePath(fileName); // name of file to write
var outStream:FileStream = new FileStream();
// open output file stream in WRITE mode
outStream.open(outFile, FileMode.WRITE);
// write out the file
outStream.writeBytes(data, 0, data.length);
// close it
outStream.close();
}
In addition, you must have Flash Player 10 and a Flex Gumbo SDK installed in your Flex Builder 3.
You can also have a look the following example:
http://blog.flexexamples.com/2008/08/25/saving-files-locally-using-the-filereference-classs-save-method-in-flash-player-10/
In Flex 3 no you can't do it unless you upload the file to the server and then download the file via a url to the desktop.
In Air or Flex 4 you can save it directly from the application to the desktop as detailed above.

Flex 3 - how to support HTTP Authentication URLRequest?

I have a Flex file upload script that uses URLRequest to upload files to a server. I want to add support for http authentication (password protected directories on the server), but I don't know how to implement this - I assume I need to extend the class somehow, but on how to I'm a little lost.
I tried to modify the following (replacing HTTPService with URLRequest), but that didn't work.
private function authAndSend(service:HTTPService):void{
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("someusername:somepassword");
service.headers = {Authorization:"Basic " + encoder.toString()};
service.send();
}
I should point out that I'm not knowledgeable when it comes to ActionScript / Flex, although I have managed to successfully modify the upload script somewhat.
[Edit] - here is an update of my progress, based on the answer below, although I still cannot get this to work:
Thank you for your assistance. I've tried to implement your code but I've not had any luck.
The general behaviour I'm experiencing when dealing with HTTP authenticated locations is that with IE7 all is well but in Firefox when I attempt to upload a file to the server it displays an HTTP authentication prompt - which even if given the correct details, simply stalls the upload process.
I believe the reason IE7 is ok is down to the the session / authentication information being shared by the browser and the Flash component - however, in Firefox this is not the case and I experience the above behaviour.
Here is my updated upload function, incorporating your changes:
private function pergress():void
{
if (fileCollection.length == 0)
{
var urlString:String = "upload_process.php?folder="+folderId+"&type="+uploadType+"&feid="+formElementId+"&filetotal="+fileTotal;
if (ExternalInterface.available)
{
ExternalInterface.call("uploadComplete", urlString);
}
}
if (fileCollection.length > 0)
{
fileTotal++;
var urlRequest:URLRequest = new URLRequest("upload_file.php?folder="+folderId+"&type="+uploadType+"&feid="+formElementId+"&obfuscate="+obfuscateHash+"&sessidpass="+sessionPass);
urlRequest.method = URLRequestMethod.POST;
urlRequest.data = new URLVariables("name=Bryn+Jones");
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("testuser:testpass");
var credsHeader:URLRequestHeader = new URLRequestHeader("Authorization", "Basic " + encoder.toString());
urlRequest.requestHeaders.push(credsHeader);
file = FileReference(fileCollection.getItemAt(0));
file.addEventListener(Event.COMPLETE, completeHandler);
file.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
file.addEventListener(ProgressEvent.PROGRESS, onUploadProgress);
file.upload(urlRequest);
}
}
As stated above, I seem to be experiencing the same results with or without the amendments to my function.
Can I ask also where the crossdomain.xml should be located - as I do not currently have one and am unsure where to place it.
The syntax is a little different for URLRequest, but the idea's the same:
private function doWork():void
{
var req:URLRequest = new URLRequest("http://yoursite.com/yourservice.ext");
req.method = URLRequestMethod.POST;
req.data = new URLVariables("name=John+Doe");
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("yourusername:yourpassword");
var credsHeader:URLRequestHeader = new URLRequestHeader("Authorization", "Basic " + encoder.toString());
req.requestHeaders.push(credsHeader);
var loader:URLLoader = new URLLoader();
loader.load(req);
}
A couple of things to keep in mind:
Best I can tell, for some reason, this only works where request method is POST; the headers don't get set with GET requests.
Interestingly, it also fails unless at least one URLVariables name-value pair gets packaged with the request, as indicated above. That's why many of the examples you see out there (including mine) attach "name=John+Doe" -- it's just a placeholder for some data that URLRequest seems to require when setting any custom HTTP headers. Without it, even a properly authenticated POST request will also fail.
Apparently, Flash player version 9.0.115.0 completely blocks all Authorization headers (more information on this one here), so you'll probably want to keep that in mind, too.
You'll almost surely have to modify your crossdomain.xml file to accommodate the header(s) you're going to be sending. In my case, I'm using this, which is a rather wide-open policy file in that it accepts from any domain, so in your case, you might want to limit things a bit more, depending on how security-conscious you are.
crossdomain.xml:
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
<allow-http-request-headers-from domain="*" headers="Authorization" />
</cross-domain-policy>
... and that seems to work; more information on this one is available from Adobe here).
The code above was tested with Flash player 10 (with debug & release SWFs), so it should work for you, but I wanted to update my original post to include all this extra info in case you run into any issues, as the chances seem (sadly) likely that you will.
Hope it helps! Good luck. I'll keep an eye out for comments.
The FileReference.upload() and FileReference.download() methods do not support the URLRequest.requestHeaders parameter.
http://livedocs.adobe.com/flex/2/langref/flash/net/URLRequest.html
If you want to upload a file, you just need to send the correct headers and the content of file using URLRequest via UploadPostHelper class. This works 100%, i am using this class to upload generated images and CSV files, but you could upload any kind of file.
This class simply prepares the request with headers and content as if you would be uploading the file from a html form.
http://code.google.com/p/as3asclublib/source/browse/trunk/net/UploadPostHelper.as?r=118
_urlRequest = new URLRequest(url);
_urlRequest.data = "LoG";
_urlRequest.method = URLRequestMethod.POST;
_urlRequest.requestHeaders.push(new URLRequestHeader("X-HTTP-Code-Override", "true"));
_urlRequest.requestHeaders.push(new URLRequestHeader("pragma", "no-cache"));
initCredentials();
_loader.dataFormat = URLLoaderDataFormat.BINARY;
//this creates a security problem, putting the content type in the headers bypasses this problem
//_urlRequest.contentType = 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary();
_urlRequest.requestHeaders.push( new URLRequestHeader( 'Cache-Control', 'no-cache' ) );
_urlRequest.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary()));
_urlRequest.data = UploadPostHelper.getPostData("file.csv", param[1]);
_loader.load(_urlRequest);
I'm not sure about this but have you tried adding username:password# to the beginning of your url?
"http://username:password#yoursite.com/yourservice.ext"
var service : HTTPService = new HTTPService ();
var encoder:Base64Encoder = new Base64Encoder();
encoder.insertNewLines = false;
encoder.encode("user:password");
service.headers = {Authorization:"Basic " + encoder.toString()};
service.method = HTTPRequestMessage.POST_METHOD;
service.request = new URLVariables("name=John+Doe");
service.addEventListener(FaultEvent.FAULT,error_handler );
service.addEventListener(ResultEvent.RESULT,result_handler);
service.url = 'http://blah.blah.xml?'+UIDUtil.createUID();
service.send();
Seemingly similar problem was solved here. I urge you to also check the Flexcoders post linked to in the first post.
The problem was that FireFox uses a separate browser window instance to send the file upload request. The solution is to manually attach the session id to the request url. The session id is not attached as a regular GET variable, but with a semicolon (the reason for this syntax is unknown to me).
Flash is very limited in terms of what sort of headers you can pass with an http request (and it changes between browsers and OSes). If you get this to work on one browser/OS, make sure you test it on the others.
The best thing to do is not mess with HTTP headers.
We have the same issue (uploading to Picasa Web Albums from flash) and post through a proxy on our server. We pass the extra headers through as post parameters and our proxy does the right thing.
"http://username:password#yoursite.com/yourservice.ext"
This doesn't work in IE (http://www.theregister.co.uk/2004/01/30/ms_drop_authentication_technique/) and doesn't seem to work in Chrome either.
probably not usable in Flash
Here is a work-around when using ASP.Net based in part on the work here.
I built a component that dynamically writes Flex objects to the page so they can be used in UpdatePanels. Message me if you want they code. To solve the above problem in pages where authentication cookies will need to be sent by URLRequest, I add the values in as flashVars.
This code only works in my object, but you get the idea
Dictionary<string, string> flashVars = new Dictionary<string, string>();
flashVars.Add("auth", Request.Cookies["LOOKINGGLASSFORMSAUTH"].Value);
flashVars.Add("sess", Request.Cookies["ASP.NET_SessionId"].Value);
myFlexObject.SetFlashVars(flashVars);
Then in the Flex Object, check for the params
if (Application.application.parameters.sess != null)
sendVars.sess= Application.application.parameters.sess;
if (Application.application.parameters.auth != null)
sendVars.au= Application.application.parameters.auth;
request.data = sendVars;
request.url = url;
request.method = URLRequestMethod.POST;
Finally stuff the cookies in on global.asax BeginRequest
if (Request.RequestType=="POST" && Request.Path.EndsWith("upload.aspx"))
{
try
{
string session_param_name = "sess";
string session_cookie_name = "ASP.NET_SESSIONID";
string session_value = Request.Form[session_param_name]; // ?? Request.QueryString[session_param_name];
if (session_value != null) { UpdateCookie(session_cookie_name, session_value); }
}
catch (Exception) { }
try
{
string auth_param_name = "au";
string auth_cookie_name = FormsAuthentication.FormsCookieName;
string auth_value = Request.Form[auth_param_name];// ?? Request.QueryString[auth_param_name];
if (auth_value != null) { UpdateCookie(auth_cookie_name, auth_value); }
}
catch (Exception) { }
}
Hope this help someone avoid the 6 hours I just spent addressing this. Adobe has closed the issue as unresolvable, so this was my last resort.

Resources