Pour pouvoir envoyer des fichiers un peu gros via WCF (plus de 10Ko dans mon cas) il y a 2 solutions :

  1. Modifier la configuration WCF pour permettre une transmission de données importantes : nous avons alors le risque de dépasser un jour cette limite, le temps de transmission va s’allonger pour chaque fichiers.
  2. Découper les fichiers à l’envoi et les reconstituer à la réception

Dans mon cas, j’avais besoin de découper les fichiers ayant une taille importante dans un dossier depuis un outils de gestion de déploiement, de les envoyer via WCF vers un service distant, puis de les reconstituer en ligne de commande lors du lancement d’un script de mise à jour. Il existe sur le marché des outils permettant de découper et reconstituer des fichiers en ligne de commande ou via une interface graphique, mais je n’ai pas trouvé d’outils permettant de le faire en code d’un coté et en ligne de commande de l’autre coté!

L’idée est donc d’avoir une assembly C# qui fait ce travail. Cet assembly pouvant être appelé depuis un projet console (pour la ligne de commande) et depuis un projet windows form (pour la gestion du déploiement). Une contrainte que j’ai eu également étant la taille : je ne devais pas dépasser les 10Ko.

Voici le résultat, le code complet est disponible à la fin de cet article avec une version compilée.

  1. Le projet
      Créer un projet Console pour pouvoir lancer le découpage/reconstitution en ligne de commande :
    image
    Dans cet exemple je me suis mis en .Net 4.0 mais le code peut être compilé en .Net 3.5 (utilisation de Linq donc à minima 3.5)

      Ajouter un projet ClassLibrary : c’est ici que sera le code de découpage/reconstitution des fichiers
    image
    La classe sera une “static” pour faciliter l’utilisation :
    namespace DarkFileSplitterCore
    {
      public static class SplitMerge
      {
        public enum Action
        {
          Merge, Split
        }
        public static void JustDoIt(string SourceFolder, string DestinationFolder, Action action, bool OverwriteDestination = true, int MaxBytesPerFile = 10000)
        {
          //...
    
  2. Un peu de récursivité
      Le but n’étant pas de découper 1 fichier unique mais un dossier, on va devoir parcourir les sous-dossiers et les fichiers qui les composent.
    public static void JustDoIt(string SourceFolder, string DestinationFolder, Action action, bool OverwriteDestination = true, int MaxBytesPerFile = 10000)
    {
      #region Prepare Folders
      if (!System.IO.Directory.Exists(SourceFolder))
        throw new ArgumentException("Source folder does not exist!", "SourceFolder");
      if (OverwriteDestination)
      {
        if (System.IO.Directory.Exists(DestinationFolder))
          System.IO.Directory.Delete(DestinationFolder, true);
        System.IO.Directory.CreateDirectory(DestinationFolder);
      }
      #endregion
      foreach (var folder in System.IO.Directory.GetDirectories(SourceFolder))
        JustDoIt(folder, Path.Combine(DestinationFolder, folder.Replace(SourceFolder, "").TrimStart('\\')), action, OverwriteDestination, MaxBytesPerFile);
    
  3. Le Split
      Reste donc à parcourir les fichiers du dossier source et de les découper :
    foreach (var file in System.IO.Directory.GetFiles(SourceFolder))
    {
      byte[] data = System.IO.File.ReadAllBytes(file); //Chargement du fichier en mémoire
      string DestinationFile = Path.Combine(DestinationFolder, file.Split('\\').Last());
      if (data.Length < MaxBytesPerFile)
      {
        //Le fichier est plus petit que la limite, écriture direct sans "split"
        System.IO.File.WriteAllBytes(DestinationFile, data);
      }
      else
      {
        //Génération des fichiers avec la taille limite
        int FileIndex = 0;
        while (data.Length > MaxBytesPerFile)
        {
          System.IO.File.WriteAllBytes(string.Format("{0}.{1:00000}.DarkFile", DestinationFile, FileIndex), data.Take(MaxBytesPerFile).ToArray());
          FileIndex++;
          data = data.Skip(MaxBytesPerFile).ToArray(); //on retire du tableau les données déjà sauvées
        }
        if (data.Length != 0) //Le reste à écrire, ce fichier sera plus petit que la limite
          System.IO.File.WriteAllBytes(string.Format("{0}.{1:00000}.DarkFile", DestinationFile, FileIndex), data);
      }
    }
    
  4. Le Merge
      Il n’y a qu’a parcourir les fichiers et régénérer les fichiers, un peu de code :
    List<byte> data = new List<byte>();
    foreach (var file in System.IO.Directory.GetFiles(SourceFolder))
    {
      if (file.EndsWith(".DarkFile"))
      {
        int FileIndex = int.Parse(file.Replace(".DarkFile", "").Split('.').Last());
        string OriginalFile = file.Replace(string.Format(".{0:00000}.DarkFile", FileIndex), "");
        if (FileIndex == 0)
          data = new List<byte>();
        data.AddRange(System.IO.File.ReadAllBytes(file));
        //On écrit à chaque boucle le fichier "cumulé", ceci évite d'avoir à tester le changement de fichier dans la boucle
        //Bien sur on pourrais optimiser en détectant le changement de fichier (OriginalFile)
        //et en écrivant dans le fichier les datas cumulées...
        System.IO.File.WriteAllBytes(Path.Combine(DestinationFolder, OriginalFile.Split('\\').Last()), data.ToArray());
      }
      else  
      {
        //Fichier non découpé
        System.IO.File.Copy(file, Path.Combine(DestinationFolder, file.Split('\\').Last()));
      }
    }
    

 

Tout ceci permet d’avoir :
- une dll / assembly capable de découper/reconstituer des fichiers dans des dossiers/sous-dossiers
- une ligne de commande pour réaliser ces opérations (voir le code source complet pour un exemple)

La dernière étape est donc la transmission après découpage du dossier via WCF, mais ceci est un autre sujet Clignement d'œil

 

Code source complet : [LINK]
EXE & DLL : [LINK]
Code + version compilé : [LINK]