Loupe

Web API : exporter une liste de blobs sous la forme de fichiers dans un zip

Un petit poste d’astuce qui pourra vous faire gagner du temps. Se servir de blob est bien pratique lorsque l’on veut stocker des informations (la définition de niveaux d’un jeu par exemple). Pour les récupérer on peut le faire de façon unitaire mais récupérer une archive avec tous les fichiers est quand même plus pratique.

 

La technique que j’utilise est la suivante :

  • Je récupère le container de blobs,
  • Je lance les tâches de lecture du contenu des blobs et je les stockent dans un dictionnaire,
  • Je créé une archive zip et j’ajoute, blob par blob les différentes entrées,
  • Je retourne le contenu dans la réponse HTTP.

La compression ZIP est réalisée à l’aide du package ‘ICSharpCode.SharpZipLib” disponible sur Nuget.

Vous noterez que je n’attends pas séquentiellement la lecture de chaque blob mais que je lance tous les téléchargements d’un coup.

private async Task<HttpResponseMessage> ListAsZip()
{

    try
    {
        //Récupération du container
        var container = await GetBlobContainerAsync().ConfigureAwait(false);

        var blobs = container.ListBlobs().OfType<CloudBlockBlob>();

        //Créations des tasks de lecture des contenus dans un dico
        var contentsTasks = blobs
            .ToDictionary(
            //Clef
            b => b.Name,
            //Valeur
            b => container.GetBlockBlobReference(b.Name).DownloadTextAsync());


        //Création d'un flux d'écriture zip
        var memoryStream = new MemoryStream();
        ZipOutputStream zipOutputStream = new ZipOutputStream(memoryStream);

        zipOutputStream.IsStreamOwner = false;

        foreach (var kVp in contentsTasks)
        {
            //Récupération du contenu du blob
            string blobContent = await kVp.Value;

            //Petite vérification d'usage
            if (string.IsNullOrEmpty(blobContent)) { continue; }

            var buffer = Encoding.UTF8.GetBytes(blobContent);

            //Création de l'entrée ZIP dans l'archive
            ZipEntry entry = new ZipEntry(ZipEntry.CleanName(kVp.Key + ".lvl"));
            entry.Size = buffer.Length; // On donne la taille d'origine que l'on connait
            entry.DateTime = DateTime.Now;
            zipOutputStream.PutNextEntry(entry);

            //Ecriture complète du contenu
            zipOutputStream.Write(buffer, 0, buffer.Length);
            zipOutputStream.Flush();

        }

        // On flushe le tout et on ferme les 
        zipOutputStream.Flush();
        zipOutputStream.Close();
        zipOutputStream.Dispose();

        memoryStream.Seek(0, SeekOrigin.Begin);

        //Création du content de la réponse en dumpant le flux mémoire
        var content = new ByteArrayContent(memoryStream.ToArray());

        //On assigne les bons headers de type de fichier
        content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");

        //On retourne un fichier
        content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
        {
            FileName = "levels.zip",
        };

        // On créé la réponse finale
        var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = content };
        return response;

    }
    catch (Exception e)
    {
        //En cas d'erreur, on retourne le message dans une 404
        return Request.CreateResponse(HttpStatusCode.NotFound, e.Message);
    }
}

 

Je déploye ma web api sur Azure mobile Services et voici quelques erreurs que j’ai rencontrées en chemin :

  • Utiliser StreamContent ou PushStreamContent semble me retourne une erreur 402 une fois déployé : j’utilise donc un ByteArrayContent à la place
  • Ne pas renseigner la taille du fichier d’origine semble poser problème
  • Ne pas vérifier que le nom donné à l’entrée Zip est correct (ZipEntry.CleanName) peut poser problème
  • Bien penser à flusher et fermer les flux manipulés.

 

En espérant que cela vous fera gagner du temps :)

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus