web-dev-qa-db-fra.com

Comment compresser un fichier en C # sans utiliser d'API tierce?

Je suis à peu près sûr que ce n'est pas un doublon, alors ne m'attendez pas une minute.

Comment puis-je programmer (C #) compresser un fichier (sous Windows) sans utiliser de bibliothèques tierces? J'ai besoin d'un appel Windows natif ou quelque chose comme ça; Je n'aime vraiment pas l'idée de démarrer un processus, mais je le ferai si je le dois absolument. Un appel PInovke serait beaucoup mieux.

En cas d'échec, laissez-moi vous dire ce que j'essaie vraiment d'accomplir: j'ai besoin de la possibilité de permettre à un utilisateur de télécharger une collection de documents en une seule requête. Des idées sur la façon d'accomplir ceci?

150
Esteban Araya

Utilisez-vous .NET 3.5? Vous pouvez utiliser la classe ZipPackage et les classes associées. Cela ne se limite pas à zipper une liste de fichiers car il faut un type MIME pour chaque fichier ajouté. Cela pourrait faire ce que vous voulez.

J'utilise actuellement ces classes pour un problème similaire afin d'archiver plusieurs fichiers liés dans un seul fichier pour le téléchargement. Nous utilisons une extension de fichier pour associer le fichier téléchargé à notre application de bureau. Un petit problème que nous avons rencontré est qu’il n’est pas possible d’utiliser un outil tiers tel que 7-Zip pour créer les fichiers Zip, car le code côté client ne peut pas l’ouvrir. ZipPackage ajoute un fichier caché décrivant le type de contenu de chaque fichier de composant et ne peut pas ouvrir un fichier Zip si ce fichier de type de contenu est manquant.

83
Brian Ensink

Comment puis-je programmer (C #) Zip un fichier (sous Windows) sans utiliser des bibliothèques tierces?

Si vous utilisez le Framework 4.5+, il existe maintenant les classes ZipArchive et ZipFile .

using (ZipArchive Zip = ZipFile.Open("test.Zip", ZipArchiveMode.Create))
{
    Zip.CreateEntryFromFile(@"c:\something.txt", "data/path/something.txt");
}

Vous devez ajouter des références à:

  • System.IO.Compression
  • System.IO.Compression.FileSystem

Pour le ciblage .NET Core net46, vous devez ajouter des dépendances pour 

  • System.IO.Compression 
  • System.IO.Compression.ZipFile

Exemple project.json:

"dependencies": {
  "System.IO.Compression": "4.1.0",
  "System.IO.Compression.ZipFile": "4.0.1"
},

"frameworks": {
  "net46": {}
}

Pour .NET Core 2.0, il suffit d'ajouter une simple instruction using:

  • using System.IO.Compression;
256
GalacticJello

J'étais dans la même situation, souhaitant .NET au lieu d'une bibliothèque tierce. En tant qu'autre affiche mentionnée ci-dessus, il ne suffit pas d'utiliser la classe ZipPackage (introduite dans .NET 3.5). Un fichier supplémentaire DOIT être inclus dans l'archive pour que le ZipPackage fonctionne. Si ce fichier est ajouté, le package Zip résultant peut être ouvert directement à partir de l'Explorateur Windows - aucun problème.

Tout ce que vous avez à faire est d’ajouter le fichier [Content_Types] .xml à la racine de l’archive avec un nœud "Par défaut" pour chaque extension de fichier à inclure. Une fois ajouté, je pouvais parcourir le package à partir de l'Explorateur Windows ou décompresser par programme et lire son contenu.

Vous trouverez plus d’informations sur le fichier .xml [Content_Types] à l’adresse suivante: http://msdn.Microsoft.com/en-us/magazine/cc163372.aspx

Voici un exemple du fichier [Content_Types] .xml (qui doit être nommé exactement):

<?xml version="1.0" encoding="utf-8" ?>
<Types xmlns=
    "http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="xml" ContentType="text/xml" /> 
  <Default Extension="htm" ContentType="text/html" /> 
  <Default Extension="html" ContentType="text/html" /> 
  <Default Extension="rels" ContentType=
    "application/vnd.openxmlformats-package.relationships+xml" /> 
  <Default Extension="jpg" ContentType="image/jpeg" /> 
  <Default Extension="png" ContentType="image/png" /> 
  <Default Extension="css" ContentType="text/css" /> 
</Types>

Et le C # pour créer un fichier Zip:

var zipFilePath = "c:\\myfile.Zip"; 
var tempFolderPath = "c:\\unzipped"; 

    using (Package package = ZipPackage.Open(zipFilePath, FileMode.Open, FileAccess.Read)) 
    { 
        foreach (PackagePart part in package.GetParts()) 
        { 
            var target = Path.GetFullPath(Path.Combine(tempFolderPath, part.Uri.OriginalString.TrimStart('/'))); 
            var targetDir = target.Remove(target.LastIndexOf('\\')); 

            if (!Directory.Exists(targetDir)) 
                Directory.CreateDirectory(targetDir); 

            using (Stream source = part.GetStream(FileMode.Open, FileAccess.Read)) 
            { 
                source.CopyTo(File.OpenWrite(target)); 
            } 
        } 
    } 

Remarque: 

11
Joshua
    private static string CompressFile(string sourceFileName)
    {
        using (ZipArchive archive = ZipFile.Open(Path.ChangeExtension(sourceFileName, ".Zip"), ZipArchiveMode.Create))
        {
            archive.CreateEntryFromFile(sourceFileName, Path.GetFileName(sourceFileName));
        }
        return Path.ChangeExtension(sourceFileName, ".Zip");
    }
5
FLICKER

Pour une application .NET 2.0, j'ai utilisé SharpZipLib . Facile à utiliser et open source. 

4
Tim Scarborough

Basé sur la réponse de Simon McKenzie à cette question , je suggérerais d'utiliser une paire de méthodes comme celle-ci:

    public static void ZipFolder(string sourceFolder, string zipFile)
    {
        if (!System.IO.Directory.Exists(sourceFolder))
            throw new ArgumentException("sourceDirectory");

        byte[] zipHeader = 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 };

        using (System.IO.FileStream fs = System.IO.File.Create(zipFile))
        {
            fs.Write(zipHeader, 0, zipHeader.Length);
        }

        dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
        dynamic source = shellApplication.NameSpace(sourceFolder);
        dynamic destination = shellApplication.NameSpace(zipFile);

        destination.CopyHere(source.Items(), 20);
    }

    public static void UnzipFile(string zipFile, string targetFolder)
    {
        if (!System.IO.Directory.Exists(targetFolder))
            System.IO.Directory.CreateDirectory(targetFolder);

        dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
        dynamic compressedFolderContents = shellApplication.NameSpace(zipFile).Items;
        dynamic destinationFolder = shellApplication.NameSpace(targetFolder);

        destinationFolder.CopyHere(compressedFolderContents);
    }
}
1
mccdyl001

On dirait que Windows pourrait vous laisser faire ceci ...

Malheureusement, je ne pense pas que vous allez commencer à mettre en place un processus séparé à moins de passer à un composant tiers. 

0
Dave Swersky

Ajoutez ces 4 fonctions à votre projet:

        public const long BUFFER_SIZE = 4096;
    public static void AddFileToZip(string zipFilename, string fileToAdd)
    {
        using (Package Zip = global::System.IO.Packaging.Package.Open(zipFilename, FileMode.OpenOrCreate))
        {
            string destFilename = ".\\" + Path.GetFileName(fileToAdd);
            Uri uri = PackUriHelper.CreatePartUri(new Uri(destFilename, UriKind.Relative));
            if (Zip.PartExists(uri))
            {
                Zip.DeletePart(uri);
            }
            PackagePart part = Zip.CreatePart(uri, "", CompressionOption.Normal);
            using (FileStream fileStream = new FileStream(fileToAdd, FileMode.Open, FileAccess.Read))
            {
                using (Stream dest = part.GetStream())
                {
                    CopyStream(fileStream, dest);
                }
            }
        }
    }
    public static void CopyStream(global::System.IO.FileStream inputStream, global::System.IO.Stream outputStream)
    {
        long bufferSize = inputStream.Length < BUFFER_SIZE ? inputStream.Length : BUFFER_SIZE;
        byte[] buffer = new byte[bufferSize];
        int bytesRead = 0;
        long bytesWritten = 0;
        while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) != 0)
        {
            outputStream.Write(buffer, 0, bytesRead);
            bytesWritten += bytesRead;
        }
    }
    public static void RemoveFileFromZip(string zipFilename, string fileToRemove)
    {
        using (Package Zip = global::System.IO.Packaging.Package.Open(zipFilename, FileMode.OpenOrCreate))
        {
            string destFilename = ".\\" + fileToRemove;
            Uri uri = PackUriHelper.CreatePartUri(new Uri(destFilename, UriKind.Relative));
            if (Zip.PartExists(uri))
            {
                Zip.DeletePart(uri);
            }
        }
    }
    public static void Remove_Content_Types_FromZip(string zipFileName)
    {
        string contents;
        using (ZipFile zipFile = new ZipFile(File.Open(zipFileName, FileMode.Open)))
        {
            /*
            ZipEntry startPartEntry = zipFile.GetEntry("[Content_Types].xml");
            using (StreamReader reader = new StreamReader(zipFile.GetInputStream(startPartEntry)))
            {
                contents = reader.ReadToEnd();
            }
            XElement contentTypes = XElement.Parse(contents);
            XNamespace xs = contentTypes.GetDefaultNamespace();
            XElement newDefExt = new XElement(xs + "Default", new XAttribute("Extension", "sab"), new XAttribute("ContentType", @"application/binary; modeler=Acis; version=18.0.2application/binary; modeler=Acis; version=18.0.2"));
            contentTypes.Add(newDefExt);
            contentTypes.Save("[Content_Types].xml");
            zipFile.BeginUpdate();
            zipFile.Add("[Content_Types].xml");
            zipFile.CommitUpdate();
            File.Delete("[Content_Types].xml");
            */
            zipFile.BeginUpdate();
            try
            {
                zipFile.Delete("[Content_Types].xml");
                zipFile.CommitUpdate();
            }
            catch{}
        }
    }

Et utilisez-les comme ceci:

foreach (string f in UnitZipList)
{
    AddFileToZip(zipFile, f);
    System.IO.File.Delete(f);
}
Remove_Content_Types_FromZip(zipFile);
0
Teemo