Utiliser URLLoader et ByteArray pour charger un millier d’images en 2sec (fin)

Dans cet article : Utiliser URLLoader et ByteArray pour charger un millier d’images en 2sec je me contentais de présenter un test du systeme de chargement binaire qui fonctionnait dans une simple classe document (Main).

Ce coup ci, voici les classes complètes, aucun besoin de tripatouiller (modifier) le code.
Les seules modifications qui pourraient être utile dépend directement du projet ou ces classes seront utilisés … tel que passer des données supplémentaires pour lier une image a un titre/texte/N° .. etc
Les classes permettent d’utiliser le chargement binaire pour tous types d’images (gif/jpg/png/swf) reconnu par Flash.
J’ai ajouté une classe évènement pour faciliter la gestion des …. évènements :D

Voir tout en bas pour le projet FlashDevelop complet.


Un test grandeur nature avec 848 images placées sur ce site (représente 2,03 Mo (2 135 934 octets) de données).
Entre mon PC et ce site, le temps oscille entre 0.8s et 2s pour le chargement complet + conversion de ces 848 images.


La classe ChargeImgBinaire :

/* ----------------------------------------------------------------------------
*
*
*                        CLASSE ChargeImgBinaire v1.0
*
*
* Flash >= AS3
* ©Lorenzo le 03/08/2010
*
 ------------------------------------------------------------------------------ */
package lol.data{
 
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	import lol.event.ChargeImgBinaireEvent;
 
	// -----------------------------------------------------------------------------------
	// Évenements
	// -----------------------------------------------------------------------------------
 
	/**
	 * Propagé pendant la progression du chargement.
	 * @eventType lol.event.ChargeImgBinaireEvent.PROGRES
	 */
	[Event(name = "progres", type = "lol.event.ChargeImgBinaireEvent.PROGRES")]
 
	/**
	 * Propagé lors d'une erreur de conversion.
	 * @eventType lol.event.ChargeImgBinaireEvent.ERREUR_CONVERSION
	 */
	[Event(name = "erreurConversion", type = "lol.event.ChargeImgBinaireEvent.ERREUR_CONVERSION")]
 
	/**
	 * Propagé lors d'une erreur de conversion (coupe le script).
	 * @eventType lol.event.ChargeImgBinaireEvent.ERREUR_CONVERSION_FATAL
	 */
	[Event(name = "erreurConversionFatal", type = "lol.event.ChargeImgBinaireEvent.ERREUR_CONVERSION_FATAL")]
 
	/**
	 * Evenement executé lors d'une erreur de chargement du fichier.
	 * @eventType lol.event.ChargeImgBinaireEvent.ERREUR_CHARGEMENT
	 */
	[Event(name = "erreurChargement", type = "lol.event.ChargeImgBinaireEvent.ERREUR_CHARGEMENT")]
 
	/**
	 * Propagé quand tout est fini (chargement/conversion).
	 * @eventType lol.event.ChargeImgBinaireEvent.FIN
	 */
	[Event(name = "fin", type = "lol.event.ChargeImgBinaireEvent.FIN")]
 
	/**
	 * Cette classe à pour but de permettre le chargement en masse de fichier image SWF/JPG/PNG/GIF.
	 * Toutes les données des images sont placés dans le meme fichier binaire qui est construit :
	 * -nb total images
	 * -image :
	 * 	poids image
	 * 	données image
	 *
	 * @see lol.event.ChargeImgBinaireEvent
	 *
	 * @example
	 *
	 * var chargeur:ChargeImgBinaire = new ChargeImgBinaire();
	 * chargeur.addEventListener(ChargeImgBinaireEvent.PROGRES, evtBinaireProgres);
	 * chargeur.addEventListener(ChargeImgBinaireEvent.ERREUR_CHARGEMENT, evtBinaireErreurChargement);
	 * chargeur.addEventListener(ChargeImgBinaireEvent.ERREUR_CONVERSION, evtBinaireErreurConversion);
	 * chargeur.addEventListener(ChargeImgBinaireEvent.FIN, evtBinaireFin);
	 * chargeur.lancer("http://localhost/data_img.php");
	 *
	 */
	public class ChargeImgBinaire extends EventDispatcher {
 
		// -----------------------------------------------------------------------------------
		//
		// INITIALISATION DES MEMBRES
		//
		// -----------------------------------------------------------------------------------
 
		// -----------------------------------------------------------------------------------
		// Paramètres utilisateur
		// -----------------------------------------------------------------------------------
 
		/**
		 * Le tableau des DisplayObject aprés chargement.
		 */
		private var _dpo:Array = new Array();
 
		/**
		 * L'URL pointant vers le fichier binaire
		 */
		private var _url:String = "";
 
		// -----------------------------------------------------------------------------------
		// Paramètres classe
		// -----------------------------------------------------------------------------------
 
		/**
		 * Le Loader servant de convertisseur.
		 */
		private var _convertisseur:Loader = null;
 
		/**
		 * Indique si le chargement est en cours.
		 */
		private var _chargeEnCours:Boolean = false;
 
		/**
		 * Le nombre total d'images dans le fichier
		 */
		private var _totalImg:uint = 0;
 
		/**
		 * L'image courante en court de traitement (erreur ou converti)
		 */
		private var _imgCourant:uint = 0;
 
		// -----------------------------------------------------------------------------------
		//
		// CONSTRUCTEUR
		//
		// -----------------------------------------------------------------------------------
 
		/**
		 * Constructeur de la classe ChargeImgBinaire
		 *
		 */
		public function ChargeImgBinaire() {
			super();
		}
 
		// -----------------------------------------------------------------------------------
		//
		// METHODES
		//
		// -----------------------------------------------------------------------------------
 
		/**
		 * Lancer le chargement des fichiers
		 *
		 * @return (Void)
		 *
		 * @throws IllegalOperationError Quand le chargement est déja en cours
		 */
		public function lancer(url:String = ""):void {
			if ( this._chargeEnCours == true ) {
				throw new IllegalOperationError("ChargeImgBinaire::lancer() -> Le chargement est déja en cours.");
				return;
			}
 
			this._totalImg = 0;
			this._dpo = new Array();
			this._imgCourant = 0;
 
			if ( url ) {
				this._url = url;
			}
 
			var loadGroupImg:URLLoader = new URLLoader();
			loadGroupImg.dataFormat = URLLoaderDataFormat.BINARY;
			loadGroupImg.addEventListener(Event.COMPLETE, evtLoadGroupImgComplete);
			loadGroupImg.addEventListener(IOErrorEvent.IO_ERROR, evtLoadGroupImgIOError);
			loadGroupImg.addEventListener(SecurityErrorEvent.SECURITY_ERROR, evtLoadGroupImgSecurityError);
			loadGroupImg.addEventListener(ProgressEvent.PROGRESS, evtLoadGroupImgProgress);
			loadGroupImg.load(new URLRequest(this._url));
			this._chargeEnCours = true;
		}
 
		// -----------------------------------------------------------------------------------
		//
		// EVENEMENTS
		//
		// -----------------------------------------------------------------------------------
 
		// ---------------------- URLLoader ----------------------
		private function evtLoadGroupImgProgress(ev:ProgressEvent):void {
			// progression du chargement des données binaire
			this.dispatchEvent(new ChargeImgBinaireEvent(ChargeImgBinaireEvent.PROGRES, false, false, (ev.bytesLoaded / ev.bytesTotal) * 100));
		}
 
		private function evtLoadGroupImgIOError(ev:IOErrorEvent):void {
			// erreur de chargement des données binaire
			var loadGroupImg:URLLoader = (ev.currentTarget as URLLoader);
			loadGroupImg.removeEventListener(Event.COMPLETE, evtLoadGroupImgComplete);
			loadGroupImg.removeEventListener(IOErrorEvent.IO_ERROR, evtLoadGroupImgIOError);
			loadGroupImg.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, evtLoadGroupImgSecurityError);
			loadGroupImg.removeEventListener(ProgressEvent.PROGRESS, evtLoadGroupImgProgress);
			this._chargeEnCours = false;
			this.dispatchEvent(new ChargeImgBinaireEvent(ChargeImgBinaireEvent.ERREUR_CHARGEMENT, false, false, 0, ev.text));
		}
 
		private function evtLoadGroupImgSecurityError(ev:SecurityErrorEvent):void {
			// erreur de chargement des données binaire
			var loadGroupImg:URLLoader = (ev.currentTarget as URLLoader);
			loadGroupImg.removeEventListener(Event.COMPLETE, evtLoadGroupImgComplete);
			loadGroupImg.removeEventListener(IOErrorEvent.IO_ERROR, evtLoadGroupImgIOError);
			loadGroupImg.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, evtLoadGroupImgSecurityError);
			loadGroupImg.removeEventListener(ProgressEvent.PROGRESS, evtLoadGroupImgProgress);
			this._chargeEnCours = false;
			this.dispatchEvent(new ChargeImgBinaireEvent(ChargeImgBinaireEvent.ERREUR_CHARGEMENT, false, false, 0, ev.text));
		}
 
		private function evtLoadGroupImgComplete(ev:Event):void {
			// chargement des données binaire réussi
			var loadGroupImg:URLLoader = (ev.currentTarget as URLLoader);
			loadGroupImg.removeEventListener(Event.COMPLETE, evtLoadGroupImgComplete);
			loadGroupImg.removeEventListener(IOErrorEvent.IO_ERROR, evtLoadGroupImgIOError);
			loadGroupImg.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, evtLoadGroupImgSecurityError);
			loadGroupImg.removeEventListener(ProgressEvent.PROGRESS, evtLoadGroupImgProgress);
			this._chargeEnCours = false;
 
			var ba:ByteArray = loadGroupImg.data;
			ba.endian = Endian.BIG_ENDIAN;
			try {
				// lire le nombre au début du fichier binaire
				this._totalImg = ba.readUnsignedInt();
			}catch (er:Error) {
				this.dispatchEvent(new ChargeImgBinaireEvent(ChargeImgBinaireEvent.ERREUR_CONVERSION_FATAL, false, false, 0, er.message));
				loadGroupImg.data = null;
				loadGroupImg = null;
				return;
			}
 
			var jpgLength:uint;
			var baJpg:ByteArray;
			var conversion:Loader;
			while (ba.position < ba.length) {
				try {
					// lecture du 'poids' de l'image courante
					jpgLength = ba.readUnsignedInt();
				}catch (er:Error) {
					this.dispatchEvent(new ChargeImgBinaireEvent(ChargeImgBinaireEvent.ERREUR_CONVERSION_FATAL, false, false, 0, er.message));
					loadGroupImg.data = null;
					loadGroupImg = null;
					return;
				}
 
				baJpg = new ByteArray();
				try {
					// ecriture des données binaires de l'image courante dans un ByteArray
					baJpg.writeBytes(ba, ba.position, jpgLength);
				}catch (er:Error) {
					this.dispatchEvent(new ChargeImgBinaireEvent(ChargeImgBinaireEvent.ERREUR_CONVERSION_FATAL, false, false, 0, er.message));
					loadGroupImg.data = null;
					loadGroupImg = null;
					return;
				}
				ba.position += jpgLength;
 
				// -------------------------------------
				conversion = new Loader();
				conversion.contentLoaderInfo.addEventListener(Event.COMPLETE, evtConversionComplete);
				conversion.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, evtConversionIOError);
 
				try {
					// conversion du ByteArray en DisplayObject
					conversion.loadBytes(baJpg);
				}catch (er:Error) {
					this.dispatchEvent(new ChargeImgBinaireEvent(ChargeImgBinaireEvent.ERREUR_CONVERSION, false, false, 0, er.message));
					loadGroupImg.data = null;
					loadGroupImg = null;
					return;
				}
			}
		}
 
		// ---------------------- LOADER - conversion ----------------------
		private function evtConversionIOError(ev:IOErrorEvent):void {
			// déclenché si les données binaires ne peuvent pas être recomposé
			// dans un des types supportés par flash (gif/png/jpg/swf)
			var conversion:Loader = ev.currentTarget.loader as Loader;
			conversion.contentLoaderInfo.removeEventListener(Event.COMPLETE, evtConversionComplete);
			conversion.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, evtConversionIOError);
			conversion.unload();
			conversion = null;
			this.dispatchEvent(new ChargeImgBinaireEvent(ChargeImgBinaireEvent.ERREUR_CONVERSION, false, false, 0, ev.text));
			this._imgCourant++;
			if ( this._imgCourant >= this._totalImg ) {
				this.dispatchEvent(new ChargeImgBinaireEvent(ChargeImgBinaireEvent.FIN));
			}
		}
 
		private function evtConversionComplete(ev:Event):void {
			// conversion des données binaires en un DisplayObject réussi
			var conversion:Loader = ev.currentTarget.loader as Loader;
			var dob:DisplayObject = conversion.content as DisplayObject;
			this._dpo.push(dob);
 
			conversion.contentLoaderInfo.removeEventListener(Event.COMPLETE, evtConversionComplete);
			conversion.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, evtConversionIOError);
			conversion.unload();
			conversion = null;
 
			this._imgCourant++;
			if ( this._imgCourant >= this._totalImg ) {
				this.dispatchEvent(new ChargeImgBinaireEvent(ChargeImgBinaireEvent.FIN));
			}
		}
 
		// -----------------------------------------------------------------------------------
		//
		// GET / SET
		//
		// -----------------------------------------------------------------------------------
 
		/**
		 * Le tableau des DisplayObject aprés chargement.
		 */
		public function get images():Array { return _dpo; }
 
		/**
		 * L'URL pointant vers le fichier binaire.
		 */
		public function get url():String { return _url; }
		public function set url(value:String):void {
			_url = value;
		}
 
		/**
		 * Le total des images chargés (nombre lu au début du fichier).
		 */
		public function get totalImg():uint { return _totalImg; }
	}
}

La classe ChargeImgBinaireEvent :

/* ----------------------------------------------------------------------------
*
*
*                            classe ChargeImgBinaireEvent v1.0
*
*
* Flash >= AS3
* ©Lorenzo le 03/08/2010
*
 ------------------------------------------------------------------------------- */
package lol.event {
 
	import flash.events.Event;
 
	/**
	* Gestion des évenements de la classe ChargeImgBinaire.
	* @see lol.data.ChargeImgBinaire
	*/
	public class ChargeImgBinaireEvent extends Event{
 
		// ---------------------------------------------------------------------------------------------
		// Propriétés
		// ---------------------------------------------------------------------------------------------
 
		/**
		 * Le texte d'erreur renvoyé par :
		 * ChargeImgBinaireEvent.ERREUR_CONVERSION
		 * ChargeImgBinaireEvent.ERREUR_CONVERSION_FATAL
		 * ChargeImgBinaireEvent.ERREUR_CHARGEMENT
		 */
		public var text:String = "";
 
		/**
		 * Le pourcentage de progression renvoyé par ChargeImgBinaireEvent.PROGRES
		 */
		public var pourcent:Number = 0;
 
		// ---------------------------------------------------------------------------------------------
		// Évenements
		// ---------------------------------------------------------------------------------------------
 
		/**
		 * Evenement executé pendant la progression du chargement.
		 * @eventType lol.event.ChargeImgBinaireEvent.PROGRES
		 */
		public static const PROGRES:String = "progres";
 
		/**
		 * Evenement executé lors d'une erreur de conversion.
		 * @eventType lol.event.ChargeImgBinaireEvent.ERREUR_CONVERSION
		 */
		public static const ERREUR_CONVERSION:String = "erreurConversion";
 
		/**
		 * Evenement executé lors d'une erreur de conversion (coupe le script).
		 * @eventType lol.event.ChargeImgBinaireEvent.ERREUR_CONVERSION_FATAL
		 */
		public static const ERREUR_CONVERSION_FATAL:String = "erreurConversionFatal";
 
		/**
		 * Evenement executé lors d'une erreur de chargement du fichier.
		 * @eventType lol.event.ChargeImgBinaireEvent.ERREUR_CHARGEMENT
		 */
		public static const ERREUR_CHARGEMENT:String = "erreurChargement";
 
		/**
		 * Evenement executé lorsque le chargement + conversion est fini.
		 * @eventType lol.event.ChargeImgBinaireEvent.FIN
		 */
		public static const FIN:String = "fin";
 
		// ---------------------------------------------------------------------------------------------
		// Consructeur
		// ---------------------------------------------------------------------------------------------
 
		/**
		 * Constructeur
		 *
		 * @param type Le type de l'evenement
		 * @param bubbles Indique si un événement peut se propager vers le haut (bubbling).
		 * @param cancelable Indique si le comportement associé à l’événement peut être annulé.
		 * @param pourcent Le pourcentage de progression renvoyé par ChargeImgBinaireEvent.PROGRES
		 * @param text Le texte d'erreur renvoyé par :
		 * ChargeImgBinaireEvent.ERREUR_CHARGEMENT
		 * ChargeImgBinaireEvent.ERREUR_CONVERSION
		 * ChargeImgBinaireEvent.ERREUR_CONVERSION_FATAL
		 *
		 * @return void
		 */
		public function ChargeImgBinaireEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false, pourcent:Number = 0, text:String = ""){
			super(type, bubbles, cancelable);
			this.pourcent = pourcent;
			this.text = text;
		}
 
		// ---------------------------------------------------------------------------------------------
		// Méthodes
		// ---------------------------------------------------------------------------------------------
 
		/**
		 * Clone
		 *
		 * @return Event
		 */
		override public function clone():Event{
			return new ChargeImgBinaireEvent(this.type, this.bubbles, this.cancelable, this.pourcent, this.text);
		}
 
		/**
		 * toString
		 *
		 * @return String
		 */
		override public function toString():String{
			return formatToString("ChargeImgBinaireEvent", "type", "bubbles", "cancelable", "eventPhase");
		}
	}
}

Le script PHP « imgBinaire.php » :

<?php
function getExt($fichier){
	$tmp = explode(".", $fichier);
	if( count($tmp) == 0 ){
		return "";
	}
	return strtolower($tmp[count($tmp) - 1]);
}
 
 
 
 
// Limiter la recherche des fichiers aux extensions définis (séparés par une virgule).
define("LIMITE_EXT", "gif,png,jpg,jpeg,swf");
$limiteExt = explode(",", strtolower(LIMITE_EXT));
 
 
// Le repertoire a lister
$rep = '../img/';
 
// le tableau qui contiendra la liste des fichiers
$tbImg = array();
 
 
 
if( !is_dir($rep) || !is_readable($rep) ){
	exit("erreur");
}
 
// --- récupérer la liste des fichiers
$rsDir = opendir($rep) or exit();
while( false !== ($fichier = readdir($rsDir)) ){
	$cheminFichier = $rep . $fichier;
	if( !is_file($cheminFichier) || !is_readable($cheminFichier) ){
		continue;
	}
	if( LIMITE_EXT && !in_array(getExt($cheminFichier), $limiteExt) ){
		continue;
	}
	array_push($tbImg, $cheminFichier);
}
closedir($rsDir);
 
 
// --- création des données binaire
// V = entier signé little endian
// N = entier signé big endian
print(pack('N', count($tbImg)));// nombre d'images
foreach($tbImg as $img){
	print(pack('N', filesize($img)));// taille de l'image
	readfile($img);// données de l'image
}
 
?>


Le projet FlashDevelop contenant les classes et un test : ChargementImgBinaireParURLLoader.zip

Laisser un commentaire

*