Aller au contenu

Posts from the ‘Flex / AS3’ Category

19
mar

Upload avec barre de progression sous Flex

Pour cet article, je me suis efforcé d’aller à l’essentiel afin d’éviter tout traitement superflue qui irait à l’encontre d’une compréhension rapide et optimale. Il s’adresse donc a des développeurs qui ont une certaine maîtrise du langage mais qui ne savent pas réellement comment mettre ne place un tel système de transfert de fichier.

1. L’interface

L’interface se compose uniquement de deux éléments:

  • Un bouton ‘Parcourir…’ qui va nous permettre d’aller récupérer notre fichier ;
  • Une barre de progression, qui s’affichera au lancement de l’upload.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init();">
  <mx:Script>
      <![CDATA[
          import aruiz.CUpload;
          private function init ():void {
              new CUpload (browseButton, progressBar);
          } // init ();
      ]]>
  </mx:Script>
  <mx:Button id="browseButton" label="Parcourir..."></mx:Button>
  <mx:ProgressBar id="progressBar" label="" visible="false"></mx:ProgressBar>
</mx:Application>

Comme vous pouvez le constater, l’interface a été volontairement réduite au strict minimum. Pour notre upload, nous utiliserons la classe CUpload qui attend deux paramètres: le bouton ‘Parcourir…’ et la barre de progression (ID des éléments concernés).

2. La classe CUpload

Voici la signature de type des différentes méthodes de la classe:

  • private function initListeners ():void;
  • public function browse (e:MouseEvent):void;
  • private function selectHandler (e:Event):void;
  • private function errorHandler (e:IOErrorEvent):void;
  • private function progressHandler (e:ProgressEvent):void;
  • private function completeHandler (e:Event):void;

La plus importante est initListeners, elle va se charger comme son nom l’indique, d’initialiser tous les écouteurs d’événements. Dans ce cas précis on parle de programmation événementielle. Tout simplement parce que lors d’un upload, il y a différentes phases qu’il est important de prendre en considérations, sans quoi il serait totalement impossible de lier toutes les actions entre-elles.

  • browse est appelée lors du click sur le bouton ‘Parcourir…’, elle a en charge d’ouvrir une fenêtre qui va permettre aux utilisateurs de choisir un fichier à transférer ;
  • selectHandler est appelée lorsqu’un fichier a été choisi par l’utilisateur, l’upload peut alors commencer ;
  • errorHandler parle d’elle même, nous ne sommes pas à l’abri d’une erreur, il faut pouvoir la capturer pour stopper tout traitement et en avertir l’utilisateur ;
  • progressHandler va nous permettre de connaître l’avancement du transfert. Comment ? A l’aide du paramètre ‘e’ de type ProgressEvent qui contient toutes les informations nécessaires. Ainsi notre barre de progression sera mise à jour de la sorte: progressBar.setProgress (e.bytesLoaded, e.bytesTotal);
  • completeHandler sera appelée en fin d’upload si tout s’est correctement déroulé.

3. Coté serveur (PHP)

Les traitements sont identiques à ceux d’un upload classique en HTML/PHP. En effet, il suffit juste d’utiliser la variable globale $_FILES (un tableau associatif) qui contient toutes les informations relatifs à notre upload. Là aussi, le code source a été volontairement simplifié pour aller à l’essentiel. Gardez en tête qu’un upload quel qu’il soit reste sensible d’un point de vu de la sécurité, il faudra donc réaliser une batterie de test afin d’être sûr que les fichiers répondent bien à certains critères, notamment leur type.

Le script est appelé en fin d’upload lorsque tout a été entièrement transféré sur le serveur. Si un ajout en base ou l’envoi d’un email est nécessaire, c’est à ce niveau du code qu’il faudra ajouter des fonctionnalités supplémentaires. Inutile donc de faire appel à un script PHP supplémentaire depuis la méthode completeHandler.

4. Conclusion

Cet article n’a rien de bien extraordinaire tellement l’upload a été vu et revu sur de nombreux blogs à travers le web. Cependant, j’ai reçu une demande d’aide de la part d’un internaute, et c’est ce qui a motivé ce court développement. Le point fort de Flex, est qu’il permet de filtrer avant tout upload les types de fichiers autorisés, cela dit, rien n’empêche de cacher du code malveillant (PHP) dans une image … Durant la phase de test, j’ai noté que les fichiers de plus de 2MO n’étaient pas transférés. C’est tout à fait normal, si c’est votre cas, il va falloir changer certaines règles de configuration (php.ini).

Si vous avez des questions, le blog est fait pour ça ;)

Voici les sources!

15
juil

Fractionner un fichier dans Flex pour l’envoyer à PHP

Sous Flex, pour enregistrer un fichier tel qu’il soit coté serveur, il faut faire appel à PHP. On pourrai utiliser d’autres langages, mais comme il est le plus répandu sur le web et qu’il est très facile à manier, notre choix s’est donc fait très naturellement.

Je rédige cet article, car aujourd’hui dans la boite pour laquelle je travaille, je me suis retrouvé face à un problème de taille, les fichiers volumineux (en terme de poids) ne sont pas correctement envoyés à PHP! Je vous assure que 6Mo est plus compliqué à envoyer qu’il n’y parait, d’où l’idée de fractionner mon fichier en plusieurs parties.

Pour que les choses soient plus simples à comprendre, dans mon exemple, je pars d’un tableau contenant des images que j’ajoute dans une archive ZIP. Donc si vous avez bien suivi, c’est l’archive qui sera fractionnée en plusieurs parties pour l’envoi.

1. Coté Flex

// Fonction qui sera appelee pour l'envoi des fichiers.
// imgArray est une propriete privee de la classe, il s'agit
// tout simplement d'un tableau d'images.

public function sendFile ():void {
    var zip:FZip = new FZip ();

    for (var i:uint = 0; i < imgArray.length; ++i) {
        zip.addFile ("page"+i+".png", imgArray[i]);
    }

    var zip_out:ByteArray = new ByteArray ();
    zip.serialize (zip_out);

    partitionne (zip_out);
} // sendFile ();

var poolByteArray:Array;

// Fonction qui partitionne l'archive ZIP.
private function partitionne (bytes:ByteArray):void {
    var maxSize:uint = 1000000; // 1 MO.
    poolByteArray = new Array ();

    var lengthTotal:uint = bytes.length;

    if (lengthTotal < maxSize) {
        poolByteArray.push (bytes);
    } else {
        // On arrondi volontairement au dessus.
        var max:uint = Math.ceil ((lengthTotal/maxSize));

        var offset:uint = 0;
        var offset_tmp:uint = 0;
        var lenghtByte:uint = maxSize;

        for (var i:uint = 0; i < max; ++i) {
            offset_tmp += lenghtByte;

            // On fait bien attention a ne pas sortir du ByteArray!
            if (offset+lenghtByte > lengthTotal) {
                lenghtByte -= offset+lenghtByte-lengthTotal;
            }

            // On ajoute au pool la partie fractionnee.
            var frac:ByteArray = new ByteArray ();
            bytes.position = offset;
            bytes.readBytes (frac, 0, lenghtByte);

            poolByteArray.push (frac);

            offset = offset_tmp;
        }
    }

    send_part (0);
} // partitionne ();

// Fonction qui envoie toutes les partitions les unes
// à la suite des autres (dans le bon ordre!).
// saveid est aussi une propriete privee de la classe
// il s'agit d'un nom de fichier genere aleatoirement
// par flex.

private function send_part (index:Number):void {
    var bytes:ByteArray = poolByteArray[index];

    var zipEncoded:Base64Encoder = new Base64Encoder ();
    zipEncoded.encodeBytes (bytes);

    var variables:URLVariables = new URLVariables ();
    variables.saveid = saveid;
    variables.zipdata = zipEncoded.flush ();

    var request:URLRequest = new URLRequest ("save.php");
    request.method = URLRequestMethod.POST;

    var loader:URLLoader = new URLLoader ();
    loader.dataFormat = URLLoaderDataFormat.BINARY;

    loader.addEventListener (Event.COMPLETE, function ():void {
        if (++index < poolByteArray.length) send_part (index);
        else Alert.show ("Transfert terminé!");
    });
    loader.addEventListener (IOErrorEvent.IO_ERROR, function ():void {});

    request.data = variables;

    loader.load (request);
} // send_part ();

2. Coté PHP

$file = $_POST['saveid'].'.zip';
$handle = fopen ($file, 'a');
fwrite ($handle, base64_decode ($_POST['zipdata']));
fclose ($handle);

Et voilà! Le plus dur aura été d'élaborer l'algo de fractionnement du fichier.
J'avoue y avoir passer un peu moins de 2 heures (tests compris) pour arriver au résultat souhaité, j'éspère donc au passage aider quelques développeurs!

3
mai

Personnalisez vos polices avec l’éditeur de texte de flex

Flex intègre nativement dans ses composants un éditeur de texte de type wysiwyg (What You See Is What You Get) très simple à mettre en place dans vos applications flash. La tâche se complique un peu lorsqu’il est question d’embarquer vos polices, et ne parlons même pas de la possibilité d’afficher un aperçu de ces dernières dans le menu déroulant, pour choisir la police à utiliser.

Voir l’aperçu

1. Création des SWF qui embarquent les polices

La 1re étape consiste à créer les SWF qui embarquent vos polices, pour cela, plusieurs choix s’offrent à vous: Soit vous décidez d’utiliser l’IDE d’adobe (méthode que je ne vais pas aborder ici), soit vous le faites en ligne de commande avec le compilateur Flex sdk 3.5 (Gratuit et multiplateforme). Attention, il faut au minimum la version 3.5 de flex sdk disponible ici.

Personnellement, j’ai voulu embarquer plusieurs polices, et qui dit action répétée, dit forcément automatisation. J’ai donc mis au point un petit script PHP qui va me parcourir le contenu d’un répertoire, récupérer tous les fichiers TTF ou OTF, générer des scripts AS pour embarquer chaque police, et compiler le tout.

Voici un exemple de script AS généré automatiquement pour embarquer une police:

package {

    import flash.display.Sprite;

    public class mapolice extends Sprite {
        [Embed(source = 'mapolice.ttf', fontName = 'mapolice')]
        public static var font:Class;
    }

}

Une fois compilé, vous obtenez votre SWF, il ne vous reste plus qu’à le charger depuis votre application principale.

2. Méthode de chargement des SWF

La prochaine étape, consiste à charger tous les SWF. Vous pouvez indiquer en dur dans votre code les fichiers à charger, pour mes besoins, j’ai choisi de faire appel à un fichier XML contenant la liste de toutes mes fonts. Pourquoi un XML ? Car le jour où vous aurez besoin d’ajouter une nouvelle font, inutile de recompiler votre application, il suffira simplement de modifier votre fichier XML. Plus pratique, plus rapide … c’est bon pour la planète, mais là aussi, libre à vous de choisir votre méthode de travail. Si vous êtes certain de ne jamais avoir à ajouter de fonts supplémentaires, stocker dans un tableau la liste de tous les fichiers SWF à charger sera une bien meilleure solution!

3. Ajouter les fonts à l’éditeur de texte RichTextEditor de flex

Commencez par ajouter un event sur l’éditeur de texte:

<mx:RichTextEditor id="rte" x="10" y="10"
    title="Mon éditeur" width="540" creationComplete="init ();">
</mx:RichTextEditor>

La méthode d’initialisation:

public function init ():void {
    // On supprime la liste des polices par defauts.
    rte.fontFamilyCombo.dataProvider = [];
    // Chargement du fichier XML.
    XML = new CXml ("./fonts/fonts.xml", constructFonts);
} // init ();

L’objet CXml se contente de charger le fichier fonts.xml et de le parser, constructFonts est la fonction appelée en retour qui va charger toutes les fonts.

private function constructFonts ():void {
    var xml:XML = XML.getXML ();
    // On recupere la liste des fonts a charger.
    for each (var d:XML in xml.children ()) {
        var nDir:String = d.attribute ("name").toString ();
        if (d.attribute ("swf").toString () == "0") {
            // Police systeme embarquee depuis local.css.
            for each (var f:XML in d.children ()) {
                fontLoaded (f.attribute ("name").toString ());
            }
        } else {
            for each (var c:XML in d.children ()) {
                embedFont (nDir, c.attribute ("name").toString ());
            }
        }
    }
} // constructFonts ();

Dans le bout de code ci-dessus, vous observez qu’il faut utiliser un fichier local.css. Pourquoi me direz-vous ? Car ce fichier va nous permettre d’embarquer la police par défaut (verdana) de l’éditeur de texte (normal, bold, italic etc…), sans quoi, nos autres polices seraient inutilisables. Pour faire court, sans verdana, il devient impossible d’utiliser nos polices embarquées dans l’éditeur de texte.

Pour utiliser un fichier CSS externe:

<mx:Style source="fonts/local.css"></mx:Style>

4. Afficher l’aperçu de nos polices dans le menu déroulant de l’éditeur de texte

Dans mon code, j’ai choisi d’afficher l’aperçu uniquement lorsque toutes les fonts sont correctement chargées. Vous le verrez dans mes sources, mais lorsque tout est terminé, je fait appel à une classe qui va modifier le rendu des polices dans le menu déroulant de l’éditeur.

rte.fontFamilyCombo.itemRenderer
    = new ClassFactory (CFontStyleRenderer);

5. Conclusion

Mettre en place un tel système est plutôt difficile, et je l’avoue, je n’ai pas mis 5 minutes à découvrir tous les rouages du langage pour arriver à mes fins. C’est pourquoi je met à votre disposition toutes les sources de l’aperçu que vous avez vu en tout début d’article. Sachez qu’il est aussi possible d’embarquer plus simplement vos polices:

[Embed(source='assets/CALIBRII.TTF', fontName='fontCalibri',
    mimeType='application/x-font')]

Mais attention, vos polices seront incluses dans l’animation, qui sera de ce fait, beaucoup plus lourde et longue à charger!

Les sources