web-dev-qa-db-fra.com

Créer un fichier Zip normal par programme

J'ai vu de nombreux tutoriels sur la façon de compresser un seul fichier en c #. Mais je dois être capable de créer un fichier * .Zip normal à partir de plusieurs fichiers. Y at-il quelque chose dans .NET qui peut le faire? Que suggéreriez-vous (en gardant à l'esprit que je suis sous des règles strictes et que je ne peux pas utiliser d'autres bibliothèques)

Je vous remercie

33
anon271334

Edit: si vous utilisez .Net 4.5 ou une version ultérieure, ceci est intégré au framework

Pour les versions antérieures ou pour plus de contrôle, vous pouvez utiliser les fonctions du shell Windows telles que décrites ici dans CodeProject de Gerald Gibson Jr .

J'ai copié le texte de l'article ci-dessous tel quel (licence d'origine: domaine public )

Compresser les fichiers Zip avec Windows Shell API et C

 enter image description here

Introduction

Cet article fait suite à celui que j'ai écrit sur la décompression de fichiers Zip. Avec ce code, vous pouvez utiliser l'API Windows Shell en C # pour compresser des fichiers Zip sans avoir à afficher la fenêtre Progression de la copie présentée ci-dessus. Normalement, lorsque vous utilisez l'API Shell pour compresser un fichier Zip, une fenêtre de progression de la copie s'affiche même lorsque vous définissez les options pour indiquer à Windows de ne pas l'afficher. Pour contourner ce problème, déplacez le code de l'API Shell vers un exécutable séparé, puis lancez ce dernier à l'aide de la classe de processus .NET en veillant à définir le style de la fenêtre de processus sur 'Caché'.

Contexte

Avez-vous déjà eu besoin de compresser des fichiers Zip et avez-vous besoin d'un meilleur Zip que celui fourni avec la plupart des bibliothèques de compression gratuites existantes? C'est à dire. vous deviez compresser des dossiers et des sous-dossiers ainsi que des fichiers. Windows Zipping peut compresser plus que des fichiers individuels. Tout ce dont vous avez besoin est un moyen de demander à Windows de compresser ces fichiers Zip par programme. Bien sûr, vous pourriez dépenser 300 USD sur l’un des composants Zip du commerce, mais il est difficile de battre gratuitement si tout ce dont vous avez besoin est de compresser les hiérarchies de dossiers.

En utilisant le code

Le code suivant montre comment utiliser l'API Windows Shell pour compresser un fichier Zip. Tout d'abord, vous créez un fichier Zip vide. Pour ce faire, créez un tableau d'octets correctement construit, puis enregistrez-le en tant que fichier avec une extension «.Zip». Comment ai-je su quels octets mettre dans le tableau? Eh bien, je viens d’utiliser Windows pour créer un fichier Zip contenant un seul fichier compressé. Ensuite, j'ai ouvert le zip avec Windows et supprimé le fichier compressé. Cela m'a laissé avec un zip vide. Ensuite, j'ai ouvert le fichier Zip vide dans un éditeur hexadécimal (Visual Studio), j'ai examiné les valeurs d'octets hexadécimales et les ai converties en valeurs décimales avec Windows Calc, puis copiées ces valeurs dans mon code de tableau d'octets. Le dossier source pointe vers un dossier que vous souhaitez compresser. Le dossier de destination pointe vers le fichier Zip vide que vous venez de créer. Ce code tel quel compressera le fichier Zip, mais il affichera également la fenêtre Progression de la copie. Pour que ce code fonctionne, vous devrez également définir une référence à une bibliothèque COM. Dans la fenêtre Références, accédez à l'onglet COM et sélectionnez la bibliothèque intitulée "Microsoft Shell Controls and Automation".

//Create an empty Zip file
byte[] emptyzip = new byte[]{80,75,5,6,0,0,0,0,0, 
                  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

FileStream fs = File.Create(args[1]);
fs.Write(emptyzip, 0, emptyzip.Length);
fs.Flush();
fs.Close();
fs = null;

//Copy a folder and its contents into the newly created Zip file
Shell32.ShellClass sc = new Shell32.ShellClass();
Shell32.Folder SrcFlder = sc.NameSpace(args[0]);
Shell32.Folder DestFlder = sc.NameSpace(args[1]); 
Shell32.FolderItems items = SrcFlder.Items();
DestFlder.CopyHere(items, 20);

//Ziping a file using the Windows Shell API 
//creates another thread where the zipping is executed.
//This means that it is possible that this console app 
//would end before the zipping thread 
//starts to execute which would cause the Zip to never 
//occur and you will end up with just
//an empty Zip file. So wait a second and give 
//the zipping thread time to get started
System.Threading.Thread.Sleep(1000);

L'exemple de solution fourni avec cet article montre comment insérer ce code dans une application console, puis le lancer pour compresser le fichier Zip sans afficher la fenêtre Progression de la copie.

Le code ci-dessous montre un gestionnaire d'événements de clic de bouton contenant le code utilisé pour lancer l'application console afin qu'il n'y ait pas d'interface utilisateur pendant la compression:

private void btnUnzip_Click(object sender, System.EventArgs e)
{
    //Test to see if the user entered a Zip file name
    if(txtZipFileName.Text.Trim() == "")
    {
        MessageBox.Show("You must enter what" + 
               " you want the name of the Zip file to be");
        //Change the background color to cue the user to what needs fixed
        txtZipFileName.BackColor = Color.Yellow;
        return;
    }
    else
    {
        //Reset the background color
        txtZipFileName.BackColor = Color.White;
    }

    //Launch the Zip.exe console app to do the actual zipping
    System.Diagnostics.ProcessStartInfo i =
        new System.Diagnostics.ProcessStartInfo(
        AppDomain.CurrentDomain.BaseDirectory + "Zip.exe");
    i.CreateNoWindow = true;
    string args = "";


    if(txtSource.Text.IndexOf(" ") != -1)
    {
        //we got a space in the path so wrap it in double qoutes
        args += "\"" + txtSource.Text + "\"";
    }
    else
    {
        args += txtSource.Text;
    }

    string dest = txtDestination.Text;

    if(dest.EndsWith(@"\") == false)
    {
        dest += @"\";
    }

    //Make sure the Zip file name ends with a Zip extension
    if(txtZipFileName.Text.ToUpper().EndsWith(".Zip") == false)
    {
        txtZipFileName.Text += ".Zip";
    }

    dest += txtZipFileName.Text;

    if(dest.IndexOf(" ") != -1)
    {
        //we got a space in the path so wrap it in double qoutes
        args += " " + "\"" + dest + "\"";
    }
    else
    {
        args += " " + dest;
    }

    i.Arguments = args;


    //Mark the process window as hidden so 
    //that the progress copy window doesn't show
    i.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;    
    System.Diagnostics.Process p = System.Diagnostics.Process.Start(i);
    p.WaitForExit();
    MessageBox.Show("Complete");
}
17
stuartd

Juste une mise à jour à ce sujet pour toute autre personne qui tombe par hasard sur cette question.

À partir de .NET 4.5, vous pouvez compresser un répertoire à l'aide de System.IO.Compression dans un fichier Zip. Vous devez ajouter System.IO.Compression.FileSystem comme référence car elle n'est pas référencée par défaut. Ensuite, vous pouvez écrire:

System.IO.Compression.ZipFile.CreateFromDirectory(dirPath, zipFile);

Le seul problème potentiel est que cet assembly n'est pas disponible pour les applications du Windows Store.

60
Danny

Vous pouvez maintenant utiliser la classe ZipArchive (System.IO.Compression.ZipArchive), disponible à partir de .NET 4.5

Exemple: générer un fichier Zip de fichiers PDF

using (var fileStream = new FileStream(@"C:\temp\temp.Zip", FileMode.CreateNew))
{
    using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true))
    {
        foreach (var creditNumber in creditNumbers)
        {
            var pdfBytes = GeneratePdf(creditNumber);
            var fileName = "credit_" + creditNumber + ".pdf";
            var zipArchiveEntry = archive.CreateEntry(fileName, CompressionLevel.Fastest);
            using (var zipStream = zipArchiveEntry.Open())
                zipStream.Write(pdfBytes, 0, pdfBytes.Length);
            }
        }
    }
}
24
andersh

Voici quelques ressources à considérer: Création d'archives Zip en .NET (sans bibliothèque externe comme SharpZipLib)

Compressez vos flux avec System.IO.Packaging

Ma recommandation et ma préférence seraient d’utiliser system.io.packacking. Cela permet de conserver vos dépendances (uniquement le framework) . La publication de Jgalloway (la première référence) fournit un bon exemple d’ajout de deux fichiers à un fichier Zip. Oui, il est plus détaillé, mais vous pouvez facilement créer une façade (dans la mesure où son AddFileToZip le fait). 

HTH

20
Jake

Mes 2 centimes:

    using (ZipArchive archive = ZipFile.Open(zFile, ZipArchiveMode.Create))
    {
        foreach (var fPath in filePaths)
        {
            archive.CreateEntryFromFile(fPath,Path.GetFileName(fPath));
        }
    }

Les fichiers Zip peuvent donc être créés directement à partir de fichiers/répertoires. 

17
Guy L

Vous pouvez essayer SharpZipLib pour cela. Is is open source, code c # pur et indépendant de la plate-forme.

6
codymanix

.NET a une fonctionnalité construite pour la compression de fichiers dans le System.IO.Compression namespace. En utilisant cela, vous n’avez pas à prendre une bibliothèque supplémentaire en tant que dépendance. Cette fonctionnalité est disponible à partir de .NET 2.0.

Voici le moyen de faire la compression à partir de la page MSDN que j'ai liée: 

    public static void Compress(FileInfo fi)
    {
        // Get the stream of the source file.
        using (FileStream inFile = fi.OpenRead())
        {
            // Prevent compressing hidden and already compressed files.
            if ((File.GetAttributes(fi.FullName) & FileAttributes.Hidden)
                    != FileAttributes.Hidden & fi.Extension != ".gz")
            {
                // Create the compressed file.
                using (FileStream outFile = File.Create(fi.FullName + ".gz"))
                {
                    using (GZipStream Compress = new GZipStream(outFile,
                            CompressionMode.Compress))
                    {
                        // Copy the source file into the compression stream.
                        byte[] buffer = new byte[4096];
                        int numRead;
                        while ((numRead = inFile.Read(buffer, 0, buffer.Length)) != 0)
                        {
                            Compress.Write(buffer, 0, numRead);
                        }
                        Console.WriteLine("Compressed {0} from {1} to {2} bytes.",
                            fi.Name, fi.Length.ToString(), outFile.Length.ToString());
                    }
                }
            }
        }
4
gyurisc

Vous devriez regarder dans Zip Packages

Ils sont une version plus structurée des archives Zip normales, nécessitant des méta-trucs à la racine. Ainsi, d'autres outils Zip peuvent ouvrir un package, mais l'API Sysytem.IO.Packaging ne peut pas ouvrir tous les fichiers Zip.

4
Henk Holterman

Cela peut être fait en ajoutant une référence à System.IO.Compression et System.IO.Compression.Filesystem.

Un exemple de méthode createZipFile () peut se présenter comme suit:

public static void createZipFile(string inputfile, string outputfile, CompressionLevel compressionlevel)
        {
            try
            {
                using (ZipArchive za = ZipFile.Open(outputfile, ZipArchiveMode.Update))
                {
                    //using the same file name as entry name
                    za.CreateEntryFromFile(inputfile, inputfile);
                }
            }
            catch (ArgumentException)
            {
                Console.WriteLine("Invalid input/output file.");
                Environment.Exit(-1);
            }
}

  • inputfile = chaîne avec le nom du fichier à compresser (pour cela, par exemple, vous devez ajouter l'extension)
  • outputfile = chaîne avec le nom du fichier Zip de destination

Plus d'informations sur ZipFile Class dans MSDN

Cela peut être fait avec une seule ligne de code en utilisant seulement Dot Net. Vous trouverez ci-dessous un exemple de code copié à partir de MSDN

Étape 1: Ajoutez une référence à System.IO.Compression.FileSystem

Étape 2: suivez le code ci-dessous

        static void Main(string[] args)
        {
            string startPath = @"c:\example\start";
            string zipPath = @"c:\example\result.Zip";
            string extractPath = @"c:\example\extract";

            ZipFile.CreateFromDirectory(startPath, zipPath);

            ZipFile.ExtractToDirectory(zipPath, extractPath);
        }
0
Sujeewa

Voici du code que j'ai écrit après avoir utilisé les publications ci-dessus. Merci pour votre aide. 

Ce code accepte une liste de chemins de fichiers et crée un fichier Zip à partir de ceux-ci. 

public class Zip
{
    private string _filePath;
    public string FilePath { get { return _filePath; } }

    /// <summary>
    /// Zips a set of files
    /// </summary>
    /// <param name="filesToZip">A list of filepaths</param>
    /// <param name="sZipFileName">The file name of the new Zip (do not include the file extension, nor the full path - just the name)</param>
    /// <param name="deleteExistingZip">Whether you want to delete the existing Zip file</param>
    /// <remarks>
    /// Limitation - all files must be in the same location. 
    /// Limitation - must have read/write/edit access to folder where first file is located.
    /// Will throw exception if the Zip file already exists and you do not specify deleteExistingZip
    /// </remarks>
    public Zip(List<string> filesToZip, string sZipFileName, bool deleteExistingZip = true)
    {
        if (filesToZip.Count > 0)
        {
            if (File.Exists(filesToZip[0]))
            {

                // Get the first file in the list so we can get the root directory
                string strRootDirectory = Path.GetDirectoryName(filesToZip[0]);

                // Set up a temporary directory to save the files to (that we will eventually Zip up)
                DirectoryInfo dirTemp = Directory.CreateDirectory(strRootDirectory + "/" + DateTime.Now.ToString("yyyyMMddhhmmss"));

                // Copy all files to the temporary directory
                foreach (string strFilePath in filesToZip)
                {
                    if (!File.Exists(strFilePath))
                    {
                        throw new Exception(string.Format("File {0} does not exist", strFilePath));
                    }
                    string strDestinationFilePath = Path.Combine(dirTemp.FullName, Path.GetFileName(strFilePath));
                    File.Copy(strFilePath, strDestinationFilePath);
                }

                // Create the Zip file using the temporary directory
                if (!sZipFileName.EndsWith(".Zip")) { sZipFileName += ".Zip"; }
                string strZipPath = Path.Combine(strRootDirectory, sZipFileName);
                if (deleteExistingZip == true && File.Exists(strZipPath)) { File.Delete(strZipPath); }
                ZipFile.CreateFromDirectory(dirTemp.FullName, strZipPath, CompressionLevel.Fastest, false);

                // Delete the temporary directory
                dirTemp.Delete(true);

                _filePath = strZipPath;                    
            }
            else
            {
                throw new Exception(string.Format("File {0} does not exist", filesToZip[0]));
            }
        }
        else
        {
            throw new Exception("You must specify at least one file to Zip.");
        }
    }
}
0
Tony Dougherty