Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Lire un fichier en ressource sous Silverlight

[new:16/01/2011]Je ne sais pas pour vous, mais en tout cas à chaque fois que je dois intégrer un fichier dans une application Silverlight je suis obligé de réfléchir un peu. Faut-il mettre le fichier sur le server dans ClientBin, faut-il l’intégrer dans Visual Studio au projet et dans ce cas en mode Content, en mode Resource ? Et ensuite ? Trop de possibilités embrouillent toujours... Faisons le point !

ClientBin

Placer un fichier sur le serveur, généralement sous /ClientBin (mais ce n’est pas une obligation) est une bonne solution si la ressource est un peu “joufflue”. Cela évite de grossir le fichier xap et simplifie aussi la mise à jour de la dite ressource (juste une copie de la nouvelle version sur le serveur).

Selon ce qu’on utilise pour visualiser / traiter la ressource, on utilisera par exemple un WebClient pour la télécharger.

Les exemples ne manquent pas, alors passons à la suite...

Content ou Resource ?

Quel casse-tête ! Comment choisir ?

D’abord il faut savoir que les deux solutions seront du même type, c’est à dire que la ressource en question sera placée dans le xap et qu’elle viendra l’alourdir. Cela tranche avec la solution précédente et fait une grande différence. Mais bien souvent, pour des fichiers de taille restreinte, choisir de placer la ressource dans le xap simplifie beaucoup le déploiement et accélère l’accès à la ressource : si le xap est lancé c’est qu’il est arrivé, et avec lui la ressource en question. Elle sera donc disponible immédiatement comme un fichier local, ce qui évite aussi la gestion asynchrone du WebClient.

Mais alors, Content ou Resource ?

J’y viens. Dans le cas du mode Content (qu’on choisit dans les propriétés de la ressource sous VS), le fichier sera copié dans le xap, à côté de la DLL de l’application (et des autres assemblages éventuels). Si vous changez l’extension du xap en “zip”, vous pourrez ressortir facilement la ressource, voire la changer facilement.

Si vous décidez d’utiliser le mode Resource, le fichier sera stocké selon un mode classique dans les ressources de l’application. S’agissant d’une DLL (une application Silverlight n’a pas de “exe”) on retrouvera ainsi la ressource dans la section Resource de la DLL. Plus difficile à extraire et à mettre à jour sans une recompilation. Pour voir la ressource, il faudra extraire la DLL du xap puis utiliser un outil comme Reflector.

Bon, alors, quel mode ?

Franchement, s’il est facile de trancher entre ressource externe (sur le serveur) ou interne, édicter une règle absolue pour orienter le choix entre le mode Resource ou Content est plus délicat.

Disons que le mode Resource est une sorte d’obligation (pour simplifier les choses) s’il s’agit d’une ressource utilisée par une DLL qui n’est pas la DLL principale de l’application. Par exemple vous avez dans votre application une seconde DLL qui contient l’accès aux données et celle-ci est capable de fournir des données de test en mode conception. Ces données de test sont stockées dans un fichier XML. Si vous le placez en mode Content, il faudra copier le fichier dans le xap, ce qui peut être oublié puisqu’on travaille principalement sur la DLL de l’application (les DLL utilitaires étant généralement créées et déboguer avant). Dans un tel cas je vous conseille de placer la ressource en mode Resource. Le fichier XML sera intégré à la DLL d’accès aux données, le fichier ne sera jamais visible et seule la DLL des données sera déployée. C’est plus propre, moins sujet à oubli.

Mais en dehors de ce cas de figure j’avoue que proposer une réponse toute faite n’est pas évidente. Alors tentons de résumer  :

  • Le mode ressource : Le fichier est intégré à la DLL. Cela est utilisable aussi bien pour le projet principal que pour des librairies placées à l’intérieur ou à l’extérieur du package (le xap). De cette façon vous êtes certains que le ressource sera disponible immédiatement pour l’application. Ce mode est parfait à condition  de ne pas avoir à  mettre à jour la ressource après la compilation. L’autre avantage est la simplification du déploiement et la diminution des dépendances puisque la ressource est cachée dans sa DLL hôte.
  • Le mode Content : Le fichier est inclus dans le xap mais pas à l’intérieur d’une DLL. Cela est plus pratique si la même ressource est utilisée par plusieurs DLL du même xap par exemple (et évite ainsi sa duplication). Le fichier n’est pas compilé avec une DLL mais il faut savoir qu’il n’est pas juste ajouté dans le xap, il existe des références codées dans les métadonnées de ce dernier et on ne peut pas faire non plus n’importe quoi... La principale raison d’utiliser le mode Content est donc de pouvoir mettre à jour la ressource après compilation et / ou de partager une même ressource entre plusieurs DLL du même xap.

Regardons comment accéder à un fichier intégré à l’application selon les deux modes.

Mode Content

La première chose à faire, quel que soit le mode, est de placer le fichier dans le projet. Je vous conseille de séparer les choses proprement en créant des sous-répertoires depuis VS (un répertoire pour les sons, un autre pour les images, un autre pour les textes...).

Une fois le fichier ajouté au projet il faut cliquer dessus dans l’explorateur de projet et regarder la fenêtre des propriétés. Là, il faut changer la propriété Build Action pour la mettre à “Content”.

image

Si nous prenons l’exemple d’un fichier XML placé directement dans le projet (sans créer un sous répertoire) l’ouverture peut se faire directement avec un XDocument ou un XElement :

XElement myElement = XElement.Load("TestData.xml"); 

un fichier en mode Content se comporte donc comme un fichier “local” (en supposant le xap comme un petit univers recréant une sorte de mini disque dur). Si le fichier est dans un sous-répertoire on utilisera bien entendu ce dernier dans le nom pour le charger. Vous pourrez inspecter le fichier xap et vous verrez le sous-répertoire dans le zip/xap.

Mode Resource

Le début de l’histoire est le même. C’est le Build Action qui change et qu’il faut positionner à Resource.

Prenons ici l’exemple d’un fichier texte “MonTexte.txt” placé dans le projet sans sous-répertoire dédié et que nous souhaitons lire ligne par ligne.

L’accès est un peu plus “vicieux” qu’en mode Content. D’abord parce qu’ici on ne peut passer que par des flux (Stream) et que ceux-ci ne sont accessibles que via des URI. Doublement vicieux donc. Mais cela est triplement vicieux même ! car la dite URI se doit d’avoir un format un peu spécial sinon jamais le fichier ne sera chargé...

Voici l’URI qu’il faut construire pour notre fichier texte :

Uri uri = new Uri("MonAppli;component/MonTexte.txt", UriKind.Relative)

On notera pour commencer que la chaîne de l’URI commence par le namespace de l’application (dans cet exemple “MonAppli”). Ce namespace est suivi d’un point-virgule.

Suit le mot “component” et enfin le nom du fichier. Il est précédé d’un slash car c’est en fait “component” qui est toujours suivi d’un slash.. Si le fichier avait été placé dans le sous-répertoire data du projet, on trouverait donc “...component/data/montexte.txt”.

Enfin, l’UriKind est en mode relatif (par rapport à la racine du projet dans son package).

Avoir une URI c’est bien joli. Et ça rime. Mais qu’en faire ?

Il nous faut maintenant obtenir et ouvrir un flux :

StreamResourceInfo streamInfo = Application.GetResourceStream(uri); 

En réalité ce que nous obtenons par GetResourceStream n’est pas un Stream mais un StreamResourceInfo. Le stream lui-même est une propriété de ce dernier.

Il est donc nécessaire d’accéder au flux du StreamResourceInfo pour enfin accéder au contenu fichier ressource, mais avant cela il aura été indispensable d’obtenir un lecteur de flux (StreamReader) !

Ce qui donne  :

if (null != streamInfo) 
   {     
      Stream stream = streamInfo.Stream;     
      StreamReader sr = new StreamReader(stream);
      ...

Seulement maintenant nous pouvons, via le lecteur de flux, lire ligne à ligne notre texte :

... 
      string line = String.Empty;     
       while ((line = sr.ReadLine()) != null)     
       {     
          // travail sur "line"
       } 
}

Vous noterez au passage la petite ruse syntaxique de CSharp qui permet d’utiliser le résultat d’un test sur une assignation en une seule opération (line est lue par ReadLine puis elle est comparée à null, ce qui marque la fin de fichier).

Et voilà... Un peu plus “tordu” mais comme nous l’avons vu plus haut la mise en ressource d’un fichier peut s’avérer plus intéressante que la mise en contenu ou même en ressource serveur.

Conclusion

Chacun fait s’qui lui plait-plait-plait.... disait la chanson. C’est à peu près ça. A vous de juger quelle méthode est la meilleure selon le projet.

Mais maintenant j’espère que vous saurez pourquoi vous choisissez une méthode plus que l’autre et que vous saurez vous en sortir pour accéder à vos ressources !

Stay Tuned !

blog comments powered by Disqus