web-dev-qa-db-fra.com

ZipArchive crée un fichier Zip non valide

J'essaie de créer un nouveau package Zip à partir du code avec une entrée et d'enregistrer le package Zip dans un fichier. J'essaie d'y parvenir avec la classe System.IO.Compression.ZipArchive. Je crée le package Zip avec le code suivant:

using (MemoryStream zipStream = new MemoryStream())
{
    using (ZipArchive Zip = new ZipArchive(zipStream, ZipArchiveMode.Create))
    {
        var entry = Zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        {
            sw.WriteLine(
                "Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        }

Ensuite, j'enregistre le Zip dans un fichier soit dans WinRT:

        var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("test.Zip", CreationCollisionOption.ReplaceExisting);
        zipStream.Position = 0;
        using (Stream s = await file.OpenStreamForWriteAsync())
        {
            zipStream.CopyTo(s);
        }

Ou dans .NET 4.5 normal:

        using (FileStream fs = new FileStream(@"C:\Temp\test.Zip", FileMode.Create))
        {
            zipStream.Position = 0;
            zipStream.CopyTo(fs);
        }

Cependant, je ne peux pas ouvrir les fichiers produits ni dans l'Explorateur Windows, WinRAR, etc. (j'ai vérifié que la taille du fichier produit correspond à la longueur du zipStream, donc le flux lui-même a été enregistré correctement dans le fichier.)
Suis-je en train de faire quelque chose de mal ou y a-t-il un problème avec la classe ZipArchive?

44
Mark Vincze

J'ai trouvé l'erreur - rétrospective, évidente - dans mon code. Le ZipArchive doit être supprimé pour lui faire écrire son contenu dans son flux sous-jacent. J'ai donc dû enregistrer le flux dans un fichier après la fin du bloc à l'aide de ZipArchive.
Et il était important de définir l'argument LeaveOpen de son constructeur sur true, pour qu'il ne ferme pas le flux sous-jacent. Voici donc la solution de travail complète:

using (MemoryStream zipStream = new MemoryStream())
{
    using (ZipArchive Zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
    {
        var entry = Zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        {
            sw.WriteLine(
                "Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        }
    }

    var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(
        "test.Zip",
        CreationCollisionOption.ReplaceExisting);

    zipStream.Position = 0;
    using (Stream s = await file.OpenStreamForWriteAsync())
    {
        zipStream.CopyTo(s);
    }
}
85
Mark Vincze

Sur tous vos objets Stream, vous devez rembobiner les flux depuis le début afin qu'ils puissent être lus correctement par d'autres applications à l'aide de la méthode .Seek.

Exemple:

zipStream.Seek(0, SeekOrigin.Begin);
2
Freeman

Vous pouvez suivre la même idée, uniquement dans l'ordre inverse, en utilisant le flux de fichiers comme source. J'ai fait le formulaire ci-dessous et le dossier s'est ouvert normalement:

string fileFormat = ".Zip"; // any format
string filename = "teste" + fileformat;

StorageFile zipFile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(filename,CreationCollisionOption.ReplaceExisting);

using (Stream zipStream = await zipFile.OpenStreamForWriteAsync()){

   using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create)){
        //Include your content here
   }
}
1
Robert Melo
// Create file "archive.Zip" in current directory use it as destination for Zip archive
using (var zipArchive = new ZipArchive(File.OpenWrite("archive.Zip"), 
ZipArchiveMode.Create))
{
    // Create entry inside Zip archive with name "test.txt"
    using (var entry = zipArchive.CreateEntry("test.txt").Open())
    {
        // Copy content from current directory file "test.txt" into created Zip entry 
        using (var file = File.OpenRead("test.txt"))
        {
            file.CopyTo(entry);
        }
    }
}    

En conséquence, vous obtiendrez l'archive "archive.Zip" avec un fichier d'entrée unique "test.txt". Vous avez besoin de cette cascade de using pour éviter toute interaction avec des ressources déjà éliminées.

1
Andrew Dragan

Le code complet ressemble à ceci:

var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("test.Zip",CreationCollisionOption.ReplaceExisting);
using (Stream zipStream = await zipFile.OpenStreamForWriteAsync())
{
    using (ZipArchive Zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
    {
        var entry = Zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        {
            sw.WriteLine("Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        }
    }   
}
0
Robert Melo

Dans mon cas, le problème était que je ne disposais pas "zipArchive" lorsque la tâche de fichier Zip se termine. Même si je vidais et fermais tous les flux.

Donc, soit utilisez en utilisant comme suggéré dans les réponses ci-dessus

using (var zipArchive = new ZipArchive(File.OpenWrite("archive.Zip"), 
ZipArchiveMode.Create))

Ou Éliminez la variable à la fin de la tâche ...

zipArchive.Dispose();
0
Ritesh Mishra