Apache Adobe Flex TutorialTutoriaux Adobe Flex & AIR en Français

1fév/0813

XML Embed supérieur à 500ko (size limit) dans une application Flex 2

Pour un projet personnel, il fallait que j'embarque un fichier XML de data supérieur à 500 ko (environ 1Mo). Pour cela, j'utilisais le tag:

<mx:XML id="xmlData" source="fichierXML.xml"/>

Le tag <mw:XML> permet d'inclure directement le XML à la compilation dans le SWF et d'ensuite créer des bind facilement. En testant avec le Flash Player 9.0.45.0, tout se passe bien, le XML est bien compilé avec l'application qui tourne correctement sous Firefox et Internet Explorer 7. Cependant, la compilation prenait beaucoup plus de temps, vraisemblablement pour la compression du XML par Flex.

Après installation du dernier FP (Flash Player 9.0.115.0), l'application ne se lance plus et fait directement un freeze au démarrage du navigateur. Après quelques tests, cela ne se produit que pour des fichiers XML supérieurs à 500ko environ, les fichiers dont la taille est inférieure fonctionnent très bien avec Flash Player 9.0.115.0. Si je déclare une nouvelle variable dans un fichier ActionScript 3 séparé contenant le XML, le navigateur internet plante aussi.

Il semble donc que Flash Player 9.0.115.0 n'accepte plus les variables supérieures à 500ko.

Contourner le problème du Flash Player

Pour contourner facilement ce problème et avoir des fichiers XML embedded > 500ko dans une application Flex, il faut faire passer ce XML comme un "octet-stream" (comme une image), puis faire un "cast" de la Class résultante en XML pour pouvoir profiter des possibilités de bind du XML. Cette méthode présente aussi l'avantage de compiler aussi vite que s'il on embarquait pas un XML, contrairement à une compilation avec le tag <mx:XML>.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal"
	verticalAlign="middle"
	 viewSourceURL="srcview/index.html">
	<mx:Script>
		<![CDATA[
			// fichier xml embed
			[Embed(source="xmlSampleFlex.xml",mimeType="application/octet-stream")]
			public var dataClass : Class;
			[Bindable]
			public var xmlInput:XML = XML(new dataClass); 

			// fonction des combobox permettant de récupérer le label du neoud courant pour afficher uniquement le label dans la CB
			private function getLabel(item:Object):String{
			    var valeur:String = "";
			    valeur = item.@['name'];
			    return valeur;
			}
			private function getClientLabel(item:Object):String{
			    var valeur:String = "";
			    valeur = item.client.@['name'];
			    return valeur;
			}
		]]>
	</mx:Script>
	<mx:Panel title="Embedded XML Data (>500ko)" width="600" height="400">
		<mx:ComboBox id="cbData" dataProvider="{xmlInput.person}" labelFunction="getLabel"/>
		<mx:ComboBox id="cbDataClient" dataProvider="{cbData.selectedItem.project}" labelFunction="getClientLabel"/>
		<mx:TextArea text="{'Data from embedded large XML: \n\n'+cbDataClient.selectedItem.short_desc}" width="90%" height="90%" editable="false"/>
	</mx:Panel>
</mx:Application>

Télécharger le fichier XML exemple associé (environ 1Mo)

Flex Source Code Download: Télécharger le code source complet de l'application

This movie requires Flash Player 11

Articles similaires

Commentaires (13) Trackbacks (0)
  1. Salut,
    Je tiens vraiment à vous remercier pour cet article!! c'est vraiment très intéressent surtout que ça fait des semaines que je cherches une solution à un problème pareil que j'ai rencontré.
    Sauf que j'utilise un "HTTPService" qui a comme URL un fichier XML de taille >1MO.
    Le résultat récupéré de ce service est enregistré dans un "ArrayCollection" et utiliser une autre classe (en fait je travaille sur le modèle de flexstore
    http://examples.adobe.com/flex2/inproduct/sdk/flexstore/flexstore.html
    )
    Est ce que c'est possible de faire de même pour mon fichier XML et le récupérer dans un "ArrayCollection"??

  2. Salut,
    je suppose que cela fonctionnerai oui, pourrais-tu donner plus d'infos sur ton problème (pense à mettre des balises si tu postes du code, voir ci-dessous), les messages d'erreur etc.

    fabien

  3. Merci pour être aussi rapide!!!
    et ben lorsque j'exécute mon application, le navigateur se bloque (j'ai essayé avec FireFox, IE, Opera , puis Flash Player => même résultat )
    Après un bout de temps, je recoit le message d'erreur suivant:
    Error: Error #1502: La durée d'exécution d'un script excède le délai par défaut (15 secondes).
    et je ne peux pas te décrire la façon dont je trouve mes données!! des erreurs partout, lenteur extrême de l'exécution d'une animation simple,...alors que la même application marche très bien avec un fichier XML très petit de taille.
    Voici mon code (j'utilise Adobe Flex Builder 3):

    Actionscript:
    1. [Bindable]
    2.         private var catalog:ArrayCollection;
    3.  
    4. private function startService():void
    5.         {
    6.             productService.send();
    7.  
    8.        }
    9.  
    10. private function productServiceResultHandler(event:ResultEvent):void
    11.         {
    12.             var products:ArrayCollection = event.result.racine.entreprises.entreprise;
    13.             var temp:ArrayCollection = new ArrayCollection();
    14.             var cursor:IViewCursor = products.createCursor();
    15.             while (!cursor.afterLast)
    16.             {
    17.                 var product:Product = new Product();
    18.                 product.fill(cursor.current);
    19.                 temp.addItem(product);
    20.                 cursor.moveNext();
    21.             }
    22.             catalog = temp;
    23.         }

    Actionscript:
    1. HTTPService
    2. id="productService"
    3. url="data/entreprises.xml"
    4. result="productServiceResultHandler(event)"

  4. Alors tu arrives bien à récupérer le fichier XML, c'est juste le parse en objets Product qui est long en fait (trop apparemment). Combien tu as de new Product au juste ? essaie de les traiter par lots (par tranches de x Product par exemple) sur l'évènement onEnterFrame, ça te permettra déjà d'avoir une meilleure fluidité.
    Mais cela me parait tout de même bizarre car j'ai déjà chargé/parsé des XMLs bien plus gros que cela (20Mo par exemple) en ayant des ralentissement mais sans plantage. Je pense que c'est la conversion de ton noeud entreprise en ArrayCollection qui ralentit le tout. Essaie de laisser ce noeud comme un XML (ou une XMLList, je ne connais pas ta data) et de le traiter avec e4x, ce sera sûrement plus rapide et plus lisible :)

    Fabien

  5. j'ai exactement 2027 Produits à créer!!!
    Oui, je vais essayer ça, même si je suis encore débutant en ça.
    Donc, je laisse le "HTTPService" récupérer les données du même fichier, j'ajoute (resultFormat="e4x") ??
    là je doit faire beaucoup de changement car il y a d'autres "components" qui utilisent des méthodes relatives aux "ArrayCollection"!! C'est vraiment efficace?? :-)

  6. Et bien il y aura toujours l'ArrayCollection qui sera remplie comme vous le faites actuellement, c'est juste le type de parcours qui diffère. Ce sera des for (ou des for each, c'est mieux) au lieu d'un gros while avec un curseur.

  7. S.V.P vous n'avez pas un exemple par hasard? :-)

  8. Bon voila un exemple fait en 2 minutes:

    MXML:
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="startService()">
    3.     <mx:Script>
    4.         <![CDATA[
    5.             import mx.rpc.events.ResultEvent;
    6.             import mx.collections.ArrayCollection;
    7.             [Bindable]
    8.             private var myArrColl:ArrayCollection = new ArrayCollection;
    9.            
    10.             private function startService():void{
    11.                 productService.send();
    12.             }
    13.            
    14.             private function productServiceResultHandler(event:ResultEvent):void{
    15.                 var result:XML = XML(event.result);
    16.                 for each (var product:XML in result.product){
    17.                     var prodObj:Object = new Object;
    18.                     prodObj.label = product.@label;
    19.                     prodObj.data = product.@data;
    20.                     myArrColl.addItem(prodObj);
    21.                 }
    22.             }
    23.         ]]>
    24.     </mx:Script>
    25.     <mx:HTTPService
    26.         id="productService"
    27.         url="tst.xml"
    28.         resultFormat="e4x"
    29.         result="productServiceResultHandler(event)"/>
    30.     <mx:List id="myList" dataProvider="{myArrColl}"/>
    31. </mx:Application>

    Quelques explications:
    Comme dans votre cas, j'appelle mon HTTPService pour charger le XML. Une fois qu'il est chargé, il passe dans la méthode de result, ici productServiceResultHandler. Je fais ensuite un cast de ma valeur en XML (pour être sur mais normalement, le résultat est déjà XML). Je boucle ensuite sur mes noeud "product" de mon xml. Ici, result.product me renvoie une XMLList contenant tous mes noeuds "product". Ensuite, je construit un Object (bon vous, vous avez un type Project qui contient des propriétés comme un Object donc c'est la même chose). J'assigne les propriétés de cet objet et je le met dans mon ArrayCollection avec un addItem. Pour info, voici mon XML (plus court, mais c'est pareil):

    MXML:
    1. <root>
    2.     <product label="toto" data="toto"/>
    3.     <product label="tata" data="tata"/>
    4.     <product label="tete" data="tete"/>
    5. </root>

  9. Fabien...sérieusement vous me sauvez là!!
    Merci énoooooormément!! :-)

  10. oh là là!! :-( j'ai cru que ça marche mais rien n'a changé en fait!!
    j'ai mis ceci:

    Actionscript:
    1. private function productServiceResultHandler(event:ResultEvent):void
    2.         {
    3.             var result:XML = XML(event.result);
    4.                 for each (var product:XML in result.entreprises.entreprise){
    5.                     var prodObj:Product = new Product();
    6.                     prodObj.ID = product.ID;
    7.                     prodObj.raisonSociale = product.raisonSociale;
    8.                     prodObj.sigle = product.sigle;
    9.                     prodObj.codePostal = product.codePostal;
    10.                     prodObj.gouvernorat = product.gouvernorat;
    11.                     prodObj.adresse = product.adresse;
    12.                     prodObj.contact = product.contact;
    13.                         prodObj.email = product.email;
    14.                         prodObj.fax = product.fax;
    15.                         prodObj.tel = product.tel;
    16.                         prodObj.statut = product.statut;
    17.                     prodObj.libelleProduitNSH = product.libelleProduitNSH
    18.                         prodObj.libelleGroupeProduit = product.libelleGroupeProduit;
    19.                     myArrColl.addItem(prodObj);
    20.                 }
    21.                 catalog = myArrColl;
    22.         }

    => même résultat qu'avant!!!!!!!

  11. C'est étrange. Bon et bien pour empêcher que l'application freeze, vous pouvez lancer ce traitement dans un pseudo thread AS3:
    http://blogs.adobe.com/aharui/2008/01/threads_in_actionscript_3.html
    Tout est expliqué sur cette page :)

  12. :-\ je suis bloqué là,
    est ce que vous pouvez me donnez votre e-mail pour que vous puissiez voir mieux ce que j'ai?
    Je suis vraiment coincé là :-(
    le mien c'est gz_youssef@hotmail.com
    Merci!

  13. fnicollet gmail.com, c'est marqué en bas de la page :P


Leave a comment

(required)

Aucun trackbacks pour l'instant