How to write files to a specific directory? - actionscript-3

I'm trying to let the user select a directory and then write files to that directory.
I have this code that lets a user browse for a directory:
var file:flash.filesystem.File = new flash.filesystem.File();
file.browseForDirectory("Select a directory");
file.addEventListener(Event.SELECT, selectHandler);
protected function selectHandler(event:Event):void {
// these contain the path where I want to save files to
Object(fileReference).url;
Object(fileReference).nativePath;
// how do I create a file in that directory?
}
How do I create a file in the directory that the user selects?

Your select handler code does not seem to be correct. You should get the reference of the Folder which is an object of the type File by doing event.currentTarget, and not Object or fileReference.
Next you can create a file using the FileStream class. Your selectHandler code should look like this:
protected function selectHandler(event:Event):void
{
var targetDirectory:File = event.currentTarget as File;
var file:File = targetDirectory.resolvePath("htmlFile.html");
var stream:FileStream = new FileStream();
stream.open(file, FileMode.WRITE);
stream.writeUTFBytes("Any Text you want to create");
stream.close();
}
Will work in AIR projects.
Hope this answers your question.

Related

Accessing local MP3 id3 properties with ExternalInterface

I need to run a swf inside an Extendscript (java) window in Photoshop. The Extendscript part pops up a Folder Selection dialog and lets user to select a folder. Then it sends the Folder path and the list of the .mp3 files in that folder to the swf file.
The swf can load and play the song
But it can't get the id3 information
The swf is in "Access Network Files Only" mode
I'm pretty sure it is a security issue and wonder if anyone knows of a workaround. Here is my code...
Thanks in advance.
var mp3FileList;
var folder;
var songList;
var songsArray:Array = new Array();
var mp3Array:Array = new Array();
if (ExternalInterface.available) {
ExternalInterface.addCallback("transferSongList", transferSongList);
}
//Extendscript opens a Select Folder dialog
//and sends the folder path and the list of .mp3 files inside the selected folder to this swf
function transferSongList(folder, mp3FileList){
songList=quickReplace(mp3FileList, "%20", " ");
songsArray = songList.split(",");
var mp3File:Sound = new Sound();
mp3File.load(new URLRequest(folder+"/" + songsArray[0]));
mp3File.addEventListener(Event.COMPLETE, parseSound(mp3File));
}
function parseSound(mp3):Function{
return function(e:Event):void {
//IF I REMOVE THE NEXT LINE THE SONG PLAYS
alertbox.text=mp3.id3.songName
mp3.play();
};
}
function quickReplace(source:String, oldString:String, newString:String):String
{
return source.split(oldString).join(newString);
}

FileReference.save ensuring it has extension

I need to save an XML with some settings and wanted to save them as my own filetype (.genconf for example). When i use..
var f:FileReference = new FileReference();
f.save( MyByteArray);
..I can allow the user to browse to save, but it only uses an extension if they enter in manually (which they definitely wont).
Is there a way to check the filename they enter, and amend/append it before the file is written?
Here's how i did it in the end (with AIR):
//on save clicked
public function export(e:MouseEvent){
var file:File = File.desktopDirectory;
file.browseForSave("Save");
file.addEventListener(Event.SELECT, onSaveSelect);
}
public function onSaveSelect(e:Event){
//create XML stuff here
var config:XML = new XML(<config />)
// compressing before saving
var bytes:ByteArray = new ByteArray();
bytes.writeUTFBytes( config );
bytes.compress();
//get browse location
var saveFile:File = File(e.target);
var directory:String = saveFile.url;
//check if by miracle they put the extension in, then add it
if(directory.indexOf(".genconf ") == -1){
directory += ".genconf ";
}
//use the new path with the extension
var file:File = new File();
file = file.resolvePath(directory);
var fileStream:FileStream = new FileStream();
fileStream.addEventListener(Event.CLOSE, onClose);
fileStream.openAsync(file, FileMode.WRITE);
fileStream.writeBytes(bytes);
fileStream.close();
}
I think that for Flash security reason you can not force the file extension in any way, in addition the file name typed by user is a read-only information so you can not modify it.
So for your case, you want to save files with the .genconf extension, I think that you can :
Request the file name to your user, before showing the save dialog, and then add the extension to have the default file name for the save dialog.
OR
Fix the default file name with your extension using time-stamp, randomized name, ... and demand to your user to save the file directly without editing the name.
In all cases, you will get a code like this :
const extension:String = '.genconf';
var file_ref:FileReference = new FileReference()
file_ref.save('your_data', 'default_file_name' + extension);
Hope that can help.

Zip a folder in as3 Adobe AIR

I have created an Air app that after user interaction will creat a folder with bmp's, xml and text doc's.
All works apart from making the final zip which needs to be automatic.
I have been looking for a solution to this but cant find it.
Perhaps I am just not seeing it, and if so, can someone show me please.
My original posting on this is here ---
zip many files with Air as3 flash
The closest thing I have found was this one --- zip a folder using fzip
But for some reason my coment was deleted which was --
I like this. It is the closest I have come to a working solution to my own problem. That said, I tested this and it works nicely as is. Can this script be moded to run without interaction??? I am in need of it for a program that I have written. ANY asistance is welcom........ apart from just pointing me to Adobe referance as it dose not have anything like what I need. (well that I can see or find)
So now I am re-asking the comunity.
For some reason it will work with manual selection and manual save-to, but not aotonomusly.
There must be a workround to this even if it requires another full page of script.
====================================================================
UPDATE:
For closing this off, I have finally got my solution.
You can find it here. "zip file contents have no data".
Hope that my problem can help someone in the future.
Try using the as3 commons zip library.
http://www.as3commons.org/as3-commons-zip/index.html
In order to do this you're going to need to load your directory, loop through all its contents and load each asset.
This code snippet includes a bulk loader to handle that for you.
warning
I pulled most of this code out of a project where I was doing something similar but I have not tested it as is. There may be some syntax errors!
private var zip:Zip;
zip = new Zip();
zip.addEventListener(IOErrorEvent.IO_ERROR, this.createNewZip); //creates a new zip
zip.addEventListener(Event.COMPLETE, handleZipLoaded); //loads the current zip, this is not shown here
zip.load(new URLRequest(File.applicationStorageDirectory.resolvePath("myZip.zip").url)); //path to your zip file
Method to create your new zip file
private function createNewZip(e:IOErrorEvent):void{
trace("no zip");
var stream:FileStream = new FileStream();
stream.open(File.applicationStorageDirectory.resolvePath("myZip.zip"), FileMode.WRITE);
zip.serialize(stream);
stream.close();
}
You can use this to add all items in a directory to your zip file.
private function addDirToZip():void{
var f:File = File.resolvePath("Your Dir");
//this will be called when your directory listing has loaded
f.addEventListener(FileListEvent.DIRECTORY_LISTING, handleDirLoaded);
//you can also get the dir listing inline and not use a listener
//doing it async will prevent ui lock
f.getDirectoryListingAsync();
}
Next your going to need to load all of the files
protected function handleDirLoaded(e:FileListEvent):void{
loadExternal = new Vector.<File>; //vector used to keep a handle on all files
e.target.removeEventListener(FileListEvent.DIRECTORY_LISTING, handleDirLoaded);
for(var i:int = 0 ; i < files.length ; i++){
var f:File = files[i] as File;
if(f.extension == "File Types you want"){ //you can do some file type checking here
loadExternal.push(f);
}
}
//start loading in the files
loadFile();
}
This will go through the loadExternal vector and load all files
private function loadFile():void{
currentFile = loadExternal.shift(); //returns the first item off the array
//load the file
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(Event.COMPLETE, handleLoaded);
l.load(new URLRequest(currentFile.url));
}
Once each item is loaded you can store it for addition into the zip
private function handleLoaded(e:Event):void{
var l:Loader = e.target.loader as Loader;
l.contentLoaderInfo.removeEventListener(Event.COMPLETE, handleLoaded);
//storing everything in a dictionary
assets[currentFile.name] = l.content;
//if we still have items to load go and do it all again
if(loadExternal.length != 0){
loadFile();
} else {
//now all files are loaded so lets add them to the zip
addAssetsToZip();
}
}
This is where all the loaded files actually get put into the zip and it is saved
private funcion addAssetsToZip():void{
for(var fileName:String in assets){
var ba:ByteArray = new ByteArray(); //going to write this to the zip
//make an object that holds the data and filename
var data:Object = {};
data.name = fileName;
data.content = assets[fileName];
ba.writeObject(data);
//write this file to the zip
zip.addFile(key, ba, false);
}
//and finally save everything out
zip.close();
var stream:FileStream = new FileStream();
stream.open(File.applicationStorageDirectory.resolvePath("myZip.zip"), FileMode.WRITE);
zip.serialize(stream);
stream.close();
}

File class AS3 / AIR: let user choose only save location, not file name or extension

When using browseForSave method, is it possible to let the user choose only the location of a file, and not the file name or the extension?
I'm creating an encrypted file, and I need its name and extensions not to be changed by the user.
Thx!
EDIT (AFTER SOLVED)
I was simply looking for the browseForDirectory method. Shame on me. :)
For a reference on how to open a browse dialog to choose a folder see the example here:
How to create "Browse for folder" dialog in Adobe FLEX?
once you have your directory you can piece that together with code here to save a file using the FileStream object:
http://blog.everythingflex.com/2008/02/25/file-and-filestream-within-air/
copied here since it's an external link
private function saveFile():void{
var myPattern:RegExp = / /g;
var newFileName:String = fileName.text.replace('.txt','');
if(newFileName.length > 1){
var file:File = File.desktopDirectory.resolvePath("Files/" + newFileName.replace(myPattern,'_') + ".txt");
var stream:FileStream = new FileStream()
stream.open(file, FileMode.WRITE);
var str:String = contents.text;
str = str.replace(/\r/g, File.lineEnding);
stream.writeUTFBytes(str);
stream.close();
fdg.directory = File.desktopDirectory.resolvePath("Files/");
fileName.text = "";
contents.text = "";
} else {
mx.controls.Alert.show("File name is required", "Error Saving File");
}
}
One solution would be to set the file object like this:
var f:File = File.desktopDirectory.resolvePath("*.txt");
f.addEventListener(Event.SELECT, onSelected);
f.browseForSave("save txt file");
Although user can still override that in the opened dialog, but at least the dialog shows the file extension. And later in the Event.SELECT you can check and confirm what user has selected.

Recursive Folder/Directory Copy with AS3 /Air

Is it possible to use pause/resume function to this??
source.copyTo( destination );
It would be great if you can send it at the earliest.
I found one solution here CookBook from Adobe
private function copyInto(directoryToCopy:File, locationCopyingTo:File):void
{
var directory:Array = directoryToCopy.getDirectoryListing();
for each (var f:File in directory)
{
if (f.isDirectory)
copyInto(f, locationCopyingTo.resolvePath(f.name));
else
f.copyTo(locationCopyingTo.resolvePath(f.name), true);
}
}
Or you could just use the File.copyTo() method:
var source:File = new File();
source.resolvePath( 'sourceFolder' );
var destination:File = new File();
destination.resolvePath( 'destinationFolder' );
source.copyTo( destination );
If the directories are large and you don't want your app to be stuck waiting for the copy, you can use copyToAsync, which will cause the source file to dispatch Event.COMPLETE when the job's done.
Here is the modified code from above if anyone wants to copy the entire directory; empty folders and all. Notice the "copyEmptyFolders" parameter to be used in the arguments.
//Recursivley copies directory.
private static function copyInto(directoryToCopy:File, locationCopyingTo:File, copyEmptyFolders:Boolean=true):void
{
var directory:Array = directoryToCopy.getDirectoryListing();
for each (var f:File in directory)
{
if (f.isDirectory)
{
// Copies a folder whether it is empty or not.
if( copyEmptyFolders ) f.copyTo(locationCopyingTo.resolvePath(f.name), true);
// Recurse thru folder.
copyInto(f, locationCopyingTo.resolvePath(f.name));
}
else
f.copyTo(locationCopyingTo.resolvePath(f.name), true);
}
}