Charger une image en cache
Par Cédric Tabin le vendredi 07.03.2008, 20:45 - ActionScript - Lien permanent
Je constate que (trop?) souvent des développeurs passent à coté d'un certains nombre de fonctionnalités offertes par le nouveau framework AS3. Notamment en ce qui concerne la gestion du cache (pour des images ou fichiers SWF par exemple). Voici un petit billet "rappel" à ce propos.
Le cache est une fonctionnalité intégrée dans tous les navigateurs Internet. Cela permet (en gros) d'éviter de recharger complètement une page si celle-ci n'a pas changé depuis la dernière fois qu'elle a été visitée.
En AS2, la gestion du cache était fastidieuse... Si une image devait être rechargée pour l'une ou l'autre raison, il fallait relancer un loadMovie. Il était également possible de charger la chose en cache en passant par la classe LoadVars, l'avantage étant qu'il n'est pas nécessaire de créer un clip vide temporaire :
var url:String = "myPicture.jpg"; var me:MovieClip = this; var cache:LoadVars = new LoadVars(); cache.onLoad = function(Void):Void { //chargement "réel" de l'image for (var i:Number=0 ; i<10 ; i++) { var container:MovieClip = me.createEmptyMovieClip("picture"+i, i); container.loadMovie(url); //positionnement... } } cache.load(url);
Ceci dit, il n'est jamais certain que l'image en question soit chargée dans tous les clips (le navigateur doit envoyer une requête pour chacune des images afin de s'assurer qu'elle n'a pas changé).
En AS3, la méthodologie reste la même mais par contre, une fois l'image chargée, celle-ci est réutilisable à volonté sans avoir à relancer un quelconque chargement dépendant de l'implémentation du navigateur ! Et cela se fait très simplement en combinant URLLoader et Loader (au même titre que MovieClip et LoadVars en AS2) :
var url:String = "myPicture.jpg"; var cache:URLLoader = new URLLoader(); cache.dataFormat = URLLoaderDataFormat.BINARY; cache.addEventListener(Event.COMPLETE, onLoadComplete); cache.load(new URLRequest(url)); function onLoadComplete(evt:Event):void { //récupération des données binaires var db:ByteArray = cache.data; for (var i:int=0 ; i<10 ; i++) { var ldr:Loader = new Loader(); ldr.loadBytes(db); //Contexte indépendant addChild(ldr); //positionnement... } }
Vous noterez la puissance de la méthode loadBytes de la classe Loader qui permet de charger une image du cache (tout comme un SWF) sans même que le navigateur ne le sache ! La seule chose manquante est une méthode similaire pour la classe Sound... Je ne sais pas pourquoi Adobe a décidé de ne pas implémenter cette méthode. Une idée ?
Commentaires
Merci pour l'astuce...
for (var i:int=0 ; i<10 ; i++) {Petite question, elle sert à quoi la boucle :
var ldr:Loader = new Loader();
ldr.loadBytes(db); //Contexte indépendant
addChild(ldr);
//positionnement...
}
En faisant comme ca, on attache pas 10 Loader sur la scene ?
Oups, en faite la boucle sert juste à monter l'interet du cache... La prochaine fois je réfléchirai avant de poster
Yo,
là le cache c'est la mémoire du player non?
je fais la même chose en as1-2 pour les images.
ca tue.
Salut,
Ben le souci c'est qu'on sait pas comment ce cache est implémenté en arrière-plan... je pense pas que ce soit le Flash Player qui gère ca, plutot le browser. Le player se contente probablement juste de relancer une requête
@++
Je ne connaissait pas cette technique de mise en cache avec l'objet loadvars.
J'ai voulu mettre ca en pratique dans une galerie photo ou la plupart des images sont assez lourdes, entres 2 et 4 mo.
J'ai d'abord essayé, après le chargement de chaque miniature de faire simplement un load de l'url de l'image réelle. Je me fiche d'une certaine façon de savoir quand c'est fini. J'essaye juste de mettre l'image dans le cache, si lorsque l'utilisateur clique sur la miniature la mise en cache est terminée tant mieux sinon tant pis.
Mais faire ainsi pose un problème. Apparemment faire un load bloque l'application et tant qu'il n'est pas fini l'exécution du code ne continue pas. Donc ca donne l'impression que les miniature se chargent tres lentement.
J'ai donc décidé ensuite de charger toutes mes miniatures et d'ensuite lancer le préchargement de toutes les images. Mais là encore un problème, car si on clique sur une image qui n'a pas encre été pré chargé et bien le chargement de l'image ne se fini apparemment jamais.
J'ai l'impression que LoadVars.load est bloquant comme appel, que ca ne peut aps fonctionner en tache de fond si je puis dire.
J'en suis alors a me demander : quelle différence si j'essaye de simuler une mise en cache avec un MovieClipLoader ? Ca m'embêterai de faire ca car il y a un chargement graphique de l'image et ca m'intéresse pas vraiment ... Mais bon, je me dis qu'au moins ce n'est peut être pas bloquant.
Que me conseillez vous ?
Salut,
Non l'appel à LoadVars n'est pas bloquant. Le problème en AS2, c'est que tu n'a pas la main sur la gestion du chargement et que tu ne peux pas le stopper (contrairement à AS3). Ceci dit, je ne pense pas que charger les grosses images en cache soit une bonne idée... surtout de le faire en même temps en plus (avec un LoadVars, il va charger toutes les données binaires en mémoire).
L'utilisation du système de cache est surtout lorsque tu dois réutiliser plusieurs fois la même image : par exemple sur une TileMap ou plusieurs tiles utilisent la même image d'affichage De nouveau, l'idée est de charger le minimum de données... Hors dans ton cas tu fais exactement le contraire : tu essaies de charger toutes les images en cache. Donc si l'utilisateur veut juste en voir une, il aura également toutes les autres Bref, cela ne me semble pas jutifié !
@++
Ok je comprends ce que tu veux dire.
Mais dans le cas d'une image que l'on charge plusieurs fois, le simple fait de la charger une première fois ne suffit il pas à la mettre en cache ? Dans l'exemple d'un jeu utilisant une tileMap, il n'y que la première tile utilisant l'image qui va finalement prendre du temps à charger l'image, les suivante, étant donné que l'image sera en cache suite au chargement par le moviecliploader devraient se charger très rapidement, non ?
En fait je ne comprends pas à quoi sert ce sytstème de chargement via un loadvars
Ensuite dans mon cas tu dis que ce n'est pas justifié, ben jsutement je trouve que ca l'est d'autant plus. Je m'explique, si je pouvais en tache de fond charger mes images alors ce serait tout bénéf. L'utilisateur ne se rendrait compte de rien , et même s'il charge toutes les images alors qu'il ne veut en voir qu'une ca ne causerait pas de problème. par contre, pour chaque image qu'il souhaite afficher il y aurait un véritable gain de temps. Le problème vient justement apparemment ca ne peut pas se faire en tâche de fond d'après les tests que j'ai fait, car si s'était le cas, je ne vois pas en quoi ca serait un problème de pré charger toutes les images.
Il doit y avoir des subtilités qui m'échappe ... erf !
Re,
Oui effectivement, le fait de la charger un première fois revient à la mettre en cache. Ceci dit, c'est principalement pour éviter d'avoir à créer un clip vide et le détruire après... C'est assez utile lorsqu'on utiliser des bibliothèques partagées par exemple.
De nouveau, dans ton cas, je pense pas que ce soit justifié car en voulant charger les images en tâche de fond, tu sapes la bande passante de l'utilisateur et étant donné que tu ne peux pas stopper un chargement en AS2, lorsqu'il demandera une image qui n'est pas encore chargées, il va mettre beaucoup plus de temps car d'autres images seront également chargées...
C'est beaucoup plus envisageable en AS3, car tu peux stopper le chargement (cf masapi) et donc gérer les priorités
Et encore une fois, si l'utilisateur voit un preloader pour son image, ca lui posera pas tant de problème d'attendre quelques secondes de plus pour voir l'image
@++
Re,
Ok je comprends bien ce que tu veux dire et j'en prends bonne note.
En tout cas j'aurai appris quelque chose.
Merci
Cedric,
j'utilise ce mecanisme que j'ai decouvert grace a ton blog, merci
Il y a beaucoup d'APIs dans Flex et de subtilites ... j'en decouvre tous les jours.
URLLoader et loadBytes fonctionnent tres bien mais ... dans le cas ou l'image a une date d'expiration dans le futur, est-il possible d'eviter le premier chargement de l'image ?
Peut-on extraire une image du cache sans meme la charger du serveur , si elle est deja dans le cache , avec une date d'expiration en 2200 ?
Par avance merci,
Stephane
Salut,
Ici en fait, on fait un cache manuel, pour pouvoir rapidement créer des "clone" d'image. Maintenant si ton image change, en passant dans le URLLoader, aucun problème elle sera rechargée C'est un "session-cache", uniquement pour flash. Le chargement de l'image est géré par le navigateur. Si comme moi l'utilisateur a choisi d'effacer tous les fichiers temporaires, cookies ou autre à la fermeture du naviguateur, la prochaine fois l'image du serveur sera chargée !
@++
Cedric,
merci pour ta reponse rapide, mais ... si l'utilisateur opte pour un cache permanent , pas un cache de session et que le serveur ajoute les bonnes directives HTTP pour que l'image n'expire pas avant un siecle ( car elle ne change pas ) alors dans ce cas peut-on eviter le premier chargement sur le serveur et travailler directement a partir du cache client ? Les APIs encapsulent-elles une request legere du style If-Modified-Since ?
Merci encore,
Stephane
Salut
Comment fais tu en AS2 une fois que tu a mis l'image en cache pour la re-afficher sur un clip présent sur la timeline ?
je n'arrive pas à forcer flash à utiliser son cache lors d'un movieClipLoader.loadClip
merci d'avance
seb
Salut,
Le fait est que tu ne peux pas "forcer" flash à utiliser son cache... tout dépend des configurations de l'utilisateur et de l'implémentation du player.
@++