Upload

5. Upload

In questo capitolo analizzeremo il codice ActionScript 3 che comporrà la classe di principale della nostra applicazione.

La classe documento in questione l'ho chiamata Upload, e ne riporto di seguito gli attributi :

  • browse (Button) - elemento dell'interfaccia che rappresenterà il bottone "Sfoglia";
  • status (TextField) - elemento dell'interfaccia che rappresenterà un campo di testo in cui verranno riportati gli stati dell'applicazione ed eventuali errori;
  • phpserv (PhpService) - istanza utilizzata per contattare gli script PHP una volta avvenuto l'invio del file;
  • fileRef (FileReference) - istanza utilizzata per inviare il file al server designato.

Procediamo ora analizzando il codice di tutti i metodi della classe :

  • Upload():void - costruttore della classe, inizializza l'attributo fileRef e il membro statico PhpService.scriptPath assegnandolo al percorso specificato nella classe Settings, inoltre richiama il metodo interfaccia() che vedremo subito dopo e infine aggiunge tutti i listener necessari al membro fileRef, per gestire le varie situazioni che possono verificarsi durante il tentativo di invio del file al server.
public function Upload():void
{
	PhpService.scriptPath = Settings.PHP_PATH;

	this.interfaccia();

	this.fileRef = new FileReference();
	this.fileRef.addEventListener(Event.SELECT, this.selectHandler);
	this.fileRef.addEventListener(Event.COMPLETE, this.completeHandler);
	this.fileRef.addEventListener(IOErrorEvent.IO_ERROR, this.ioErrorHandler);
	this.fileRef.addEventListener(ProgressEvent.PROGRESS, this.progressHandler);
	this.fileRef.addEventListener(HTTPStatusEvent.HTTP_STATUS, this.httpStatusHandler);
	this.fileRef.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.securityHandler);
}

  • interfaccia():void - inizializza e configura i due membri browse e status dandogli la formattazione adeguata e li aggiunge infine al filmato contenitore attraverso il metodo addChild().
private function interfaccia():void
{
	// Bottone Sfoglia
	this.browse = new Button();
	this.browse.x = 10;
	this.browse.y = 30;
	this.browse.width = 50;
	this.browse.height = 20;
	this.browse.label = "Sfoglia";
	this.browse.addEventListener(MouseEvent.CLICK, this.browseClick);
	this.addChild(this.browse);

	// Campo di testo Status
	var format:TextFormat = new TextFormat("Verdana", 10, "0x000000");

	this.status = new TextField();
	this.status.x = 10;
	this.status.y = 10;
	this.status.width = 200;
	this.status.height = 16;
	this.status.border = true;
	this.status.defaultTextFormat = format;
	this.status.text = "Nessun file selezionato.";
	this.addChild(this.status);
}

  • browseClick(evt:MouseEvent):void - listener che gestisce i click sul bottone browse.
    Nel caso in cui l'etichetta del bottone sia impostata su "Sfoglia" tenta di aprire una finestra di dialogo per sfogliare i file sul disco rigido dell'utente attraverso il metodo browse() dell'istanza fileRef, se invece l'etichetta è impostata su "Invia" tenta di inviare il file precedentemente selezionato tramite il metodo upload() dell'istanza fileRef.
private function browseClick(evt:MouseEvent):void
{
	if (this.browse.label == "Sfoglia")
	{
		try
		{
			this.fileRef.browse(Settings.ALLOWED_TYPES);
		}
		catch (error:Error)
		{
			this.status.text = "Impossibile sfogliare il disco rigido.";
		}
	}
	else if (this.browse.label == "Invia")
	{
		this.browse.enabled = false;
		this.status.text = "Invio in corso ...";
		var request:URLRequest = new URLRequest(Settings.PHP_PATH + "upload.php");

		try
		{
			this.fileRef.upload(request);
		}
		catch (error:Error)
		{
			this.browse.enabled = true;
			this.status.text = "Impossibile inviare il file.";
		}
	}
}

  • checkUpload(evt:Event):void - più avanti vedremo il codice del listener completeHandler(), il cui compito sarà quello di utilizzare la classe PhpService per contattare il servizio php verify.php.
    Il risultato ottenuto da quest ultimo script verrà gestito appunto dal metodo checkUpload.
private function checkUpload(evt:Event):void
{
	this.browse.enabled = true;
	this.browse.label = "Sfoglia";

	if (Boolean(parseInt(this.phpserv.response.success)))
		this.status.text = "File inviato con successo.";
	else
		this.status.text = "Il file non è stato accettato.";

	trace(this.phpserv.response.log);
}

  • Di seguito riporto tutti gli altri metodi, ossia i listener che si limiteranno a riportare le informazioni raccolte attraverso il campo di testo status.

/* Gestori degli eventi */
private function selectHandler(evt:Event):void
{
	this.browse.label = "Invia";
	this.status.text = evt.target.name;
}

private function completeHandler(evt:Event):void
{
	this.status.text = "File \"" + evt.target.name + "\" inviato, verifica in corso ...";

	this.phpserv = new PhpService("verify.php");
	this.phpserv.addVar("filename", evt.target.name);
	this.phpserv.addEventListener(Event.COMPLETE, this.checkUpload);
	this.phpserv.request();
}

private function ioErrorHandler(evt:IOErrorEvent):void
{
	this.status.text = "Errore di Input/Output : " + evt;
}

private function progressHandler(evt:ProgressEvent):void
{
	var pc:uint = evt.bytesLoaded * 100 / evt.bytesTotal;
	this.status.text = "Invio in corso ... ";
	this.status.appendText(String(pc) + "%");
}

private function httpStatusHandler(evt:HTTPStatusEvent):void
{
	this.status.text = "Errore HTTP : " + evt;
}

private function securityHandler(evt:SecurityErrorEvent):void
{
	this.status.text = "Violazione della sicurezza : " + evt;
}

Si conclude qui questo tutorial, spero utile.
Potete scaricare l'intero codice d'esempio da qui.

Per far si che tutto funzioni correttamente, ricordatevi di modificare i permessi alle cartelle "php" per la scrittura del file di log e alla cartella "upload" per l'archiviazione dei file.


4. Client Side

Come già accennato nell'introduzione di questo tutorial, anche per ActionScript 3 ci serviremo di una classe costruita in precedenza nella mia Guida ad ActionScript 3.

La classe in questione è PhpService.
Non ho apportato alcuna modifica a questa classe, quindi se volete ricostruirne il codice e studiarne il funzionamento vi rimando al capitolo dedicato : Classi per interrogare PHP.

Anche per ActionScript 3 ho creato un'enumerazione, ossia una classe usata come collezione di valori (costanti) che useremo per impostare rapidamente alcuni parametri standard che detteranno il funzionamento della nostra applicazione.

Questa classe prende il nome di Settings :

Settings.as
package script.as3
{
	import flash.net.FileFilter;

	public final class Settings
	{
		public static const PHP_PATH:String = "http://10.0.0.8/realizzazionesito/articoli/as3 files/script/php/";
		public static const ALLOWED_TYPES:Array = new Array
		(
			new FileFilter("Immagini", "*.jpg; *.jpeg; *.bmp; *.gif; *.png"),
			new FileFilter("Archivi", "*.zip; *.rar; *.ace; *.7z")
		);
	}
}

La costante PHP_PATH è assegnata al percorso HTTP che conterrà gli script PHP responsabili della gestione dei files inviati.

L'indirizzo del server in questo caso è impostato a 10.0.0.8 semplicemente perchè ho testato l'applicazione sul mio computer, che essende sotto rete NAT (utilizzo un Router ADSL) ha un indirizzo interno proprio.

Se lo testate sul vostro server locale probabilmente dovrete utilizzare localhost (127.0.0.1).
Una volta effettuati i test e le personalizzazioni al codice, utilizzerete direttamente l'indirizzo remoto del server come mostrato nel percorso di esempio che segue :

// ...
public static const PHP_PATH:String = "http://www.realizzazione-sito.info/script/php/";
// ...

La costante ALLOWED_TYPES è un array di istanze della classe FileFilter.
Attraverso l'impostazione di questa costante possiamo personalizzare la finestra di dialogo che l'utente utilizzerà per selezionare il file da inviare dal suo disco rigido.

Aggiungendo o rimuovendo nuovi elementi dell'array o modificando quelli da me impostati, è possibile impostare dei filtri che il browser dell'utente sfrutterà per rendere possibile solo l'invio di determinati file, servendosi delle estensioni specificate.

Nel prossimo capitolo vedremo il codice dell'ultima classe, che sarà il cuore della nostra applicazione.


3. Script PHP

In questa lezione vedremo come utilizzare le classi viste nel capitolo precedente e che compongono la nostra libreria PHP 5.

Innanzitutto mettiamo i tre file PHP che seguono in una cartella nominata classi :

  • UploadSettings.php
  • ErroreFile.php
  • File.php

Nel percorso in cui risiede la cartella classi, andremo a creare lo script PHP che verrà contattato per primo dal filmato Flash.
Lo script in questione avrà il nome "upload.php", e sarà lo script che riceverà il file inviato vero e proprio.

upload.php
<?php

	require_once("classi/File.php");

	try
	{
		$file = new File(UploadSettings::NAME, UploadSettings::FILE_PATH);
		$file->tipo(GIF, PNG, JPEG, GENERIC);
		$file->dimensione(UploadSettings::MAX_SIZE, "kb");
		$file->sposta();
	}
	catch (ErroreFile $e)
	{
		if (UploadSettings::LOG_ME)
			fwrite($file->log, "exception:'" . $e->messaggio() . "'");
	}
	catch (Exception $e)
	{
		if (UploadSettings::LOG_ME)
			fwrite($file->log, "exception:'" . $e->getMessage() . "'");
	}

?>

Il sorgente è molto breve, in quanto i controlli vengono effettuati tutti dalla classe File.
Il resto dello script non fa altro che loggare eventuali eccezioni nel caso in cui la costante LOG_ME sia impostata sul valore true.

In pratica il costruttore della classe File inizializza il file di log e lo script procede tentando di archiviare il file.
Se si verifica un'eccezione, i due blocchi catch la memorizzano nel file di log. A questo punto lo script termina e vengono quindi richiamati tutti i distruttori delle varie classi, quindi anche quello della classe File che come abbiamo visto nel capitolo precedente, scriverà una linea di formattazione nel file di log e poi lo chiuderà con fclose().

Una volta inviato il file, il filmato Flash contatterà un secondo script PHP per verificare che il file sia stato accettato e correttamente archiviato sul server.

Il secondo script prenderà il nome di "verify.php" :

verify.php
<?php

	require_once("classi/UploadSettings.php");

	$filename = UploadSettings::FILE_PATH . $_POST["filename"];

	if (file_exists($filename)) echo "success=1";
	else echo "success=0";

	if (UploadSettings::LOG_ME)
	{
		$file = fopen(UploadSettings::LOG_FILE, "r");
		$log = fread($file, filesize(UploadSettings::LOG_FILE));
		echo "&log=$log";
	}

?>

Lo script soprastante non fa altro che verificare la corretta archiviazione del file inviato ad upload.php, tramite la funzione nativa file_exists().

Una volta chiamata la funzione, restituirà il risultato al filmato Flash inviando le variabili in codifica URL tramite il costrutto echo.

Per motivi di debug, ho impostato lo script in modo che restituisca a Flash anche il contenuto del file di log, in modo da non obbligarci a scaricarlo via FTP per effettuarne la lettura.

Nel prossimo capitolo inizieremo a vedere il codice Client Side in ActionScript 3, che fornirà l'interfaccia per l'invio dei file ai nostri utenti.

2. Server Side

In questo capitolo vedremo il codice Server Side in PHP 5, responsabile della gestione e della memorizzazione del file inviato tramite l'applicazione in ActionScript 3, che studieremo invece nei capitoli successivi.

Come già anticipato nei capitoli precedenti, le classi in questione le abbiamo già viste nella Guida a PHP 5, più esattamente nel capitolo Classi per l'upload di file.

Il codice della classe ErroreFile è rimasto invariato, mentre per la classe File ho definito un distruttore e ho effettuato qualche modifica al costruttore, ma prima di introdurre queste modifiche è necessaria una premessa.

La classe FileReference non ci consente di ricevere una risposta personalizzata dallo script PHP che si occuperà dell'archiviazione del file inviato, ma ci consente solo di gestire delle determinate eccezioni tramite i blocchi try e catch.

Questo fattore ci obbliga a interrogare un secondo script PHP una volta che il file è stato inviato,per verificarne l'effettiva archiviazione sul server.
Inoltre, nel caso in cui l'archiviazione non fosse andata a buon fine, ho ampliato la classe File come ho accennato prima, inserendo nel costruttore e nel distruttore del codice che creerà un file di log sul server, in modo da poter risalire con maggior precisione al motivo per cui il file non è stato accettato.

Per prima cosa vediamo il codice PHP della nuova classe UploadSettings, una collezione di costanti che ci consentirà di configurare rapidamente il nostro script :

UploadSettings.php
<?php

	final class UploadSettings
	{
		const NAME = "Filedata";
		const MAX_SIZE = 1024; // Espresso in KiloByte
		const FILE_PATH = "C:/Appserv/www/RealizzazioneSito/articoli/as3 files/upload/";

		const LOG_ME = true;
		const LOG_FILE = "upload.log";
	}

?>

Di default la classe ActionScript FileReference, invierà il file al server con il nome Filedata, accessibile quindi da PHP tramite l'array globale $_FILES["Filedata"].
Dal momento che è possibile assegnare un nome personalizzato al file, ho creato la costante NAME nella classe PHP UploadSettings, in modo da poter riconoscere immediatamente il file inviato.

Seguono altre costanti come MAX_SIZE che definisce la dimenzione massima in kilobyte che il file potrà avere.
FILE_PATH indica la cartella del server dove dovranno essere salvati i file inviati.
Infine le costanti LOG_ME e LOG_FILE indicano rispettivamente se lo script dovrà loggare le attività di upload, e il nome del file di log.

A questo punto non ci resta che dare un'occhiata alle modifiche effettuate alla classe File.
Oltre alle modifiche a costruttore e distruttore, ho definito una nuova costante che consentirà allo script di accettare anche file di tipo generico, rappresentando il tipo MIME corrispondente alla stringa "application/octet-stream".

File.php
<?php

	require_once("ErroreFile.php");
	require_once("UploadSettings.php");

	// MIME Types
	define("GIF", "image/gif");
	define("JPEG", "image/jpeg");
	define("PNG", "image/png");
	define("PSD", "image/psd");
	define("BMP", "image/bmp");
	define("TIFF", "image/tiff");
	define("GENERIC", "application/octet-stream"); // Nuova costante
	define("FLASH", "application/x-shockwave-flash"); // Filmato Adobe Flash

	class File
	{
		public $log;
		private $file;
		private $cartella;

		public function File($nome, $cartella = "/upload/")
		{
			// Inizio frammento di nuovo codice per il log
			if (UploadSettings::LOG_ME)
			{
				$this->log = fopen(UploadSettings::LOG_FILE, "a ");
				$content = "########## " . date("d/m/Y - H:i:s", time()) . "\n";
	
				foreach ($_FILES[$nome] as $chiave => $valore)
					$content .= "$chiave:'$valore'\n";
	
				fwrite($this->log, $content);
			}
			// Fine frammento di nuovo codice per il log

			if (isset($_FILES[$nome]))
			{
				$this->file = $_FILES[$nome];
				$this->cartella = $cartella;
			}
			else
				throw new ErroreFile(UPLOAD_ERR_NO_FORM);
		}

		public function tipo()
		{
			if (!func_num_args()) throw new ErroreFile(UPLOAD_ERR_NO_TYPE);

			$tipi = func_get_args();

			foreach ($tipi as $chiave => $valore)
				if ($this->file["type"] == $valore)
					return true;

			throw new ErroreFile(UPLOAD_ERR_NO_TYPE);
		}

		public function dimensione($dim, $unita = "byte")
		{
			if (strcasecmp($unita, "byte") == 0) $dim = (int) $dim;
			else if (strcasecmp($unita, "kb") == 0) $dim = (int) $dim * 1024;
			else if (strcasecmp($unita, "mb") == 0) $dim = (int) $dim * 1024 * 1024;
			else $dim = 0;

			if ($this->file["size"] > $dim) throw new ErroreFile(UPLOAD_ERR_FORM_SIZE);
		}

		public function sposta()
		{
			$ok = @move_uploaded_file($this->file["tmp_name"], $this->cartella . $this->file["name"]);

			if (!$ok) throw new ErroreFile(UPLOAD_ERR_MOVE);
		}

		public function immagine($dir, $alt = NULL)
		{
			$dimensioni = getimagesize($dir . $this->file["name"]);
			$dimensioni = $dimensioni[3]; // height="yyy" width="xxx"

			
			$imgtag = "<img src=\"$dir" . $this->file["name"] . "\" ";
			$imgtag .= "$dimensioni ";
			$imgtag .= (strlen($alt) ? "alt=\"$alt\"" : "") . " />";

			return $imgtag;
		}

		public function __get($attributo)
		{
			if (array_key_exists($attributo, $this->file))
				return $this->file[$attributo];

			return false;
		}

		public function __toString()
		{ return $this->file["name"]; }

		// Nuovo distruttore
		// Scrive una linea di chiusura nel file di log e lo chiude
		function __destruct()
		{
			fwrite($this->log, "\n##########\n\n");
			fclose($this->log);
		}
	}

?>

Ora che la nostra libreria PHP è al completo, possiamo proseguire nel prossimo capitolo dove verranno illustrati i due script PHP che avranno il compito di comunicare direttamente con il filmato Flash.

1. Classi

Passiamo velocemente in rassegna tutte le classi che formeranno la nostra applicazione, a cominciare dagli oggetti di ActionScript 3.


Classi ActionScript 3

Richieste HTTP (Comunicazione con PHP e invio di file)

  • FileReference - Classe principale che utilizzeremo per caricare i file dell'utente sul server e che ci consentirà anche di avere a disposizione e di personalizzare le finestre di dialogo per sfogliare i file dal disco rigido.
  • URLRequest - Classe base per effettuare delle richieste HTTP e comunicare quindi con PHP, la useremo infatti per inviare il file e per controllare che il file sia stato accettato dal server.
  • FileFilter - Utilizzata per impostare dei filtri sui file che l'utente potrà caricare sul server.

Gestione degli Eventi

  • Event - Contiene le costanti base per gestire gli eventi più importanti, come l'avvenuta selezione del file da parte dell'utente o il completamente dell'invio del file sul server.
  • MouseEvent - Usata per gestire gli eventi del mouse (CLICK).
  • IOErrorEvent - Contiene la costante IO_ERROR, utilizzata nel verificarsi di un errore di lettura, scrittura o trasmissione del file, o ancora nel caso in cui il server su cui si tenta di caricare il file richieda un'autenticazione e infine nel caso in cui si tenti di utilizzare un protocollo diverso da HTTP o HTTPS.
  • ProgressEvent - Utilizzeremo questa classe per tenere traccia dello stato di caricamento del file.
  • HTTPStatusEvent - Contiene la costante HTTP_STATUS utilizzata per sollevare eccezioni nel caso in cui si verificasse un errore HTTP.
  • SecurityErrorEvent - Contiene SECURITY_ERROR, inviata quando si verifica una violazione della sicurezza.

Costruzione dell'interfaccia

  • Button - Componente di Flash CS3 che ci consente di inserire dei bottoni standard nel filmato.
  • TextField - Classe per aggiungere campi di testo nello stage.
  • TextFormat - Classe utilizzata per formattare lo stile dei campi di testo TextField.
  • MovieClip - Classe di base per creare il clip filmato con lo stage.

Classi personalizzate

  • Settings - Enumerazione utilizzata per impostare alcuni settaggi base per l'applicazione.
  • PhpService - Classe derivata da EventDispatcher, utilizzata per comunicare più facilmente con i nostri script PHP.

Classi PHP 5

Classi personalizzate

  • UploadSettings - Collezione di costanti utilizzate per configurare il funzionamento dello script.
  • ErroreFile - Derivata da Exception, utilizzata per estendere le eccezioni possibili che possono verificarsi durante l'upload di un file sul server.
  • File - Classe che offre svariati metodi per semplificare la gestione del file inviato e per controllarne il corretto formato.

Upload di File in ActionScript 3 e PHP 5

In questo tutorial vedremo come è possibile servirci di ActionScript 3 e PHP 5 per creare un'applicazione molto leggera e dinamica, che ci consentirà di inviare dei file su un server tramite delle richieste asincrone, utilizzando ossia una tecnologia che funzionerà in modo molto simile ad AJAX.

Questo significa che l'utente potrà sfruttare la nostra applicazione per inviarci dei file, senza doversi sorbire noiosi caricamenti di pagina, il che comporta anche una ridotta quantità di richieste HTTP con un conseguente alleggerimento dei nostri server.

Condividi contenuti