Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Leggere e scrivere file sul filesystem con ActionScript 3

Sfruttare le novità introdotte nella classe FileReference dal Flash Player 10
Sfruttare le novità introdotte nella classe FileReference dal Flash Player 10
Link copiato negli appunti

Una delle maggiori limitazioni di Flash, dovuta essenzialmente alla sicurezza, è sempre stata quella della lettura e soprattutto del salvataggio di file: per queste operazioni si affiancava all'applicazione Flash un supporto lato server.

La classe FileReference del Flash Player 10 - che già veniva usata per l'upload in combinata con linguaggi server-side nelle ultime versioni - è stata potenziata ed è ora possibile usarla anche per salvare dati sul computer dell'utente senza la necessità di passare da altri linguaggi.

Ovviamente per motivi di sicurezza l'operazione deve essere confermata dall'utente, che sceglierà anche il percorso locale del file.

Si tratta di una caratteristica molto interessante, specie se unita con alcune librerie esterne come la Actionscript 3 Core Library, sviluppata da Adobe e che include delle classi per l'encoding in JPG o in PNG.

L'accoppiata ci permetterebbe, ad esempio, di prendere uno screenshot della nostra applicazione (o di un componente) e salvarlo in formato JPG o PNG sul pc dell'utente. Per ottenere lo stesso comportamento con Flash CS3 e Flash Player 9 avremmo dovuto passare le informazioni dallo stage ad una applicazione lato server, che si sarebbe poi occupata di convertire i dati ricevuti da Flash in immagine.

Ora tutto è molto più semplice e in questo articolo creeremo una piccola applicazione che permetterà all'utente di caricare un'immagine, elaborarla in Flash e salvarla poi modificata, il tutto sfruttando la classe FileReference e la Actionscript 3 Core Library.

L'applicazione che svilupperemo nell'articolo

Caricare un file dal pc dell'utente

È bene precisare innanzitutto che il caricamento del file non comporta un parallelo upload del file sul server, ma il file viene letto e i caricato all'interno del player in un oggetto di tipo ByteArray, classe anch'essa introdotta in Actionscript 3. Per motivi di sicurezza infatti il player riceve solo il nome e i contenuti del file ma non ad esempio il path in cui è memorizzato.

Anzitutto creiamo un pulsante sul nostro stage e gli diamo nome istanza carica; questo pulsante dovrà aprire la finestra di scelta del file da caricare nella nostra applicazione. Il codice da utilizzare è il seguente:

var _loadFile:FileReference;

carica.addEventListener(MouseEvent.CLICK, caricaFile);

function caricaFile(evt:MouseEvent)
{
  // creiamo l'oggetto FileReference
  _loadFile = new FileReference();
  
  // associamo la funzione scegliFile alla selezione
  _loadFile.addEventListener(Event.SELECT, scegliFile);
  
  // impostiamo il filtro che permetta la scelta di sole immagini  
  var filtro:FileFilter = new FileFilter("Immagini: (*.jpeg, *.jpg, *.gif, *.png)", "*.jpeg; *.jpg; *.gif; *.png");
  
  // apriamo la finestra di scelta
  _loadFile.browse([filtro]);
}

function scegliFile(evt:Event)
{
  // azioni successive alla scelta del file
} 

I commenti dovrebbero essere sufficienti a comprendere il codice, fin qui non c'è nulla di complesso, semplicemente sfruttiamo il metodo browse della classe FileReference per aprire una finestra di scelta del file. Tale metodo accetta un parametro che possiamo usare per impostare dei filtri sulla scelta dei file, in questo caso la variabile filtro (di tipo FileFilter) permetterà la scelta di sole immagini nei formati jpg, gif o png (gli unici che il player legge nativamente).

Una volta che l'utente avrà selezionato un file, sarà chiamata la funzione scegliFile, che si occuperà di caricare i dati del file nel player:

function scegliFile(evt)
{
  // rimuoviamo l'evento di scelta file non più necessario
  _loadFile.removeEventListener(Event.SELECT, scegliFile);
  
  // creiamo il listener per l'evento COMPLETE che verrà 
  // lanciato una volta completato il caricamento del file
  _loadFile.addEventListener(Event.COMPLETE, fileCaricato);
  
  // carichiamo il file all'interno del player
  _loadFile.load();
}

function fileCaricato(evt:Event)
{
  // azioni da eseguire a caricamento completato
} 

Questa funzione carica il file grazie al metodo load e una volta completato il caricamento (e viene scatenato l'evento COMPLETE), lancia la funzione fileCaricato. Poichè non è più necessario abbiamo rimosso il listener relativo all'evento di scelta del file.

Leggere il file caricato in memoria

Esaminiamo ora come leggere i dati caricati. L'oggetto FileReference possiede la proprietà 'data'di tipo ByteArray che contiene i dati binari del file selezionato dall'utente. Quindi, per utilizzare questi dati, possiamo sfruttare i metodi della classe ByteArray.

Nel nostro caso abbiamo a che fare con un file jpg, gif o png, quindi dati direttamente interpretabili dal dal Flash Player. Questo ci evita la necessità di implementare un parser intermedio e ci permette di limitarci a mostrare l'immagine caricata sullo stage.

Anche questa volta facciamo uso di un evento, infatti utilizziamo un oggetto Loader per ottenere i dati contenuti nell'oggetto _loadFile e attendiamo che il trasferimento sia concluso.

function fileCaricato(evt:Event)
{
  // rimuoviamo il listener non più necessario
  _loadFile.removeEventListener(Event.COMPLETE, fileCaricato);
  
  // creiamo l'oggetto Loader che caricherà l'immagine
  var immagine:Loader = new Loader();
  
  // impostiamo il listener da eseguire a fine 
  // caricamento dell'immagine
  immagine.contentLoaderInfo.addEventListener(Event.COMPLETE, immaginePronta);
  
  // carichiamo i bytes dall'oggetto ByteArray
  // all'interno del Loader
  immagine.loadBytes(evt.target.data);
}

Dopo aver fatto pulizia del listener non più necessario, creiamo il nostro oggetto Loader. È utile sottolineare l'utilizzo del metodo loadBytes: stiamo caricando i dati binari ricavati dal file scelto dall'utente, ed è per questo che non usiamo il più diffuso metodo load, a cui viee associato l'URL da caricare, ma sfruttiamo quest'altro metodo che carica i byte.

Al termine del caricamento, sarà scatenato l'evento COMPLETE e lanciata la funzione immaginePronta. In questa funzione aggiungiamo prima uno sprite sullo stage, che farà da contenitore dell'immagine e poi l'immagine da esso contenuta.

Utilizziamo proprio questo sprite per modificare graficamente l'immagine, non sarebbe infatti possibile operare direttamente sul Loader. Ecco quindi le poche righe di codice necessarie all'operazione:

// dichiariamo la variabile relativa allo sprite
var contenitore:Sprite;

function immaginePronta(evt:Event)
{
  // rimuoviamo il listener non pu necessario
  LoaderInfo(evt.target).removeEventListener(Event.COMPLETE,immaginePronta);
  
  // creiamo lo sprite
  contenitore = new Sprite();
  
  // aggiungiamo l'immagine caricata allo sprite
  contenitore.addChild(evt.target.content);
  
  // aggiungiamo lo sprite allo stage
  addChild(contenitore);
}

Anche questa volta rimuoviamo il listener, ma in questo caso per poter rimuovere l'evento abbiamo dovuto ricavare il target dell'evento come oggetto LoaderInfo.

Il resto è molto semplice: dichiariamo il contenitore, di tipo Sprite e vi inseriamo l'immagine (contenuta nella proprietà content del target dell'evento). Infine aggiungiamo il contenitore allo stage.

Potremmo voler applicare un ridimensionamento dell'immagine per evitare che possa essere troppo grossa per la nostra interfaccia, in questo caso basterà agire sulle proprietà width ed height dell'oggetto contenitore.

Modifica dell'immagine

Proprio l'oggetto contenitore sarà quello che sfrutteremo anche per applicare le altre modifiche alla nostra immagine. Poichè scopo dell'articolo è vedere il caricamento e il salvataggio del file, non faremo particolari operazioni sull'immagine ma ovviamente si può ampliare a proprio piacimento l'applicazione.

In questo caso ci limteremo a convertire l'immagine in scala di grigio, sfruttando il ColorMatrixFilter; anche questa opzione la associamo ad un pulsante (con nome istanza modifica).

modifica.addEventListener(MouseEvent.CLICK,modificaImmagine);

function modificaImmagine(evt:MouseEvent)
{
  // matrice per la trasformazione in sacala di grigio
  var grigi:Array = [0.33,0.33,0.33,0,0,0.33,0.33,0.33,0,0,0.33,0.33,0.33,0,0,0,0,0,1,0];
  
  // creazione del ColorMatrixFilter
  var matriceColore:ColorMatrixFilter = new ColorMatrixFilter(grigi);
  
  // associazione della matrice allo sprite
  contenitore.filters = [matriceColore];
}

Salvare l'immagine modificata

Eccoci quindi alla novità più interessante, ovvero la possibilità di salvare il file. In questo caso, trattandosi di un'immagine dovremo ricavare l'immagine modificata in scala di grigi, convertirla grazie all'encoder della Actionscript 3 Core Library e infine salvarla, ovviamente per altri file e altri formati i passaggi "pre-salvataggio" potrebbero variare.

Esaminiamo il codice per questi passaggi, considerando che l'unico "fisso" da riusare anche in altri casi per salvare un file sarà solo quello relativo al nome del file all'oggetto FileReference salvaFile.

// importiamo l'encoder
import com.adobe.images.JPGEncoder;

salva.addEventListener(MouseEvent.CLICK,salvaImmagine);

function salvaImmagine(Evt:MouseEvent)
{
  // copiamo dallo sprite l'immagine modificata
  var immagineModificata:BitmapData = new BitmapData(contenitore.width,contenitore.height);
  immagineModificata.draw(contenitore,contenitore.transform.matrix);

  // creiamo il bytearray che conterrà l'immagine
  var immagineCodificata:ByteArray;
  
  // creiamo il JPGEncoder che codificherà l'immagine
  var jpgEncoder:JPGEncoder = new JPGEncoder(85);
  
  // inseriamo nelByteArray l'immagine codificata
  immagineCodificata = jpgEncoder.encode(immagineModificata);
  
  // creiamo l'espressione regolare per il nome del file
  var expNomeFile:RegExp = /^(?P<fileName>.*)..*$/;
  
  // aggiungiamo al nome originale del file il suffisso _gray
  var NomeFile:String = expNomeFile.exec(_loadFile.name).fileName + "_gray";
  
  // quindi l'estensione .jpg
  nomeFile+= ".jpg";
  
  // creiamo l'oggetto FileReference per il salvataggio del file
  var salvaFile:FileReference = new FileReference();
  
  // impostiamo i listener per salvataggio completo ed errore
  salvaFile.addEventListener(Event.COMPLETE, fileSalvato);
  salvaFile.addEventListener(IOErrorEvent.IO_ERROR, erroreSalvataggio);
  
  // salviamo il file
  salvaFile.save(immagineCodificata, nomeFile);
}

function fileSalvato(evt:Event)
{
  trace("File salvato con successo");
}

function erroreSalvataggio(evt:ErrorEvent)
{
  trace("Errore nel salvataggio");
} 

Per prima cosa abbiamo importato il JPGEncoder ricavato dalla Actionscript 3 Core Library (sono varie classi ed è possibile copiare nel proprio progetto solo la JPGEncoder, non è necessario averne altre nel nostro caso), quindi all'interno della funzione salvaImmagine (associata al click sul pulsante salva) eseguiamo diverse operazioni.

Per prima cosa creiamo un oggetto BitmapData delle dimensioni dello sprite contenitore, quindi vi disegniamo lo stesso sprite; notiamo che abbiamo inserito come secondo parametro la matrice di trasformazione di contenitore, questo perchè altrimenti non avremmo copiato la versione "modificata" ma l'originale (quindi senza nessun cambio di colore e partendo dalla dimensione originale).

Dopo aver preparato l'immagine, impostiamo l'oggetto immagineCodificata (di tipo ByteArray), che conterrà il risultato della codifica fatta tramite il JPGEncoder (notiamo che in questo caso abbiamo impostato per l'immagine una qualità di 85, il range va da 1 a 100), quindi impostiamo il nome del file ricavando il nome originale, aggiungendo _gray e infine l'estensione (in questo caso .jpg). L'espressione regolare expNomeFile regola l'input che l'utente può inserire per il nome del file.

Come ultima operazione, creiamo l'oggetto salvaFile (di tipo FileReference), impostiamo due listener (uno per il completamento del salvataggio e uno per gli eventuali errori) e usando il metodo save salviamo il file nella posizione scelta dall'utente, come il metodo browser infatti anche il metodo save aprirà una finestra con la quale scegliere la posizione in cui salvare il file e specificare un nome eventualmente diverso da quello impostato automaticamente.

Conclusioni

Abbiamo visto come la classe FileReference sia stata ulteriormente potenziata, inoltre grazie all'abbinamento con la classe ByteArray permette di estendere notevolmente le feature del player (ad esempio recentemente Lee Brimelow ha riportato sul suo blog alcuni esempi tra cui una classe per caricare file PSD in Flash). Queste caratteristiche aumentano sempre più le potenzialità di Flash in campo RIA, inoltre librerie come la Actionscript 3 Core Library consentono di eseguire facilmente operazioni come l'encoding in PNG o in JPEG, usando poche righe di codice.

La possibilità di caricare e salvare file senza l'ausilio di linguaggi server-side consente migliori performance (il passaggio dell'immagine al linguaggio server-side e il salvataggio tramite le librerie grafiche di quest'ultimo è più complesso del gestire il tutto all'interno del player) e permettono anche una più semplice diffusione dei propri lavori: prima del Flash Player 10 sarebbe stato necessario un hosting con supporto ASP, PHP o altro per supportare questo tipo di applicazione, mentre ora può essere usato praticamente qualsiasi hosting.

Ti consigliamo anche