Come posso ZIP un file in C #, senza API di terze parti?


175

Sono abbastanza sicuro che questo non sia un duplicato, quindi abbi pazienza per un minuto.

Come posso programmare (C #) ZIP un file (in Windows) senza usare librerie di terze parti? Ho bisogno di una chiamata nativa a Windows o qualcosa del genere; Non mi piace l'idea di avviare un processo, ma lo farò se assolutamente dovessi. Una chiamata PInovke sarebbe molto meglio.

In caso contrario, lascia che ti dica cosa sto davvero cercando di realizzare: ho bisogno della possibilità di consentire a un utente di scaricare una raccolta di documenti in una singola richiesta. Qualche idea su come realizzare questo?



1
@Chesso: Sì, da una pagina ASPX.
Esteban Araya,

1
Ho trovato utile questo esempio quando cercavo la stessa cosa qualche settimana fa: syntaxwarriors.com/2012/…
JensB,

2
Se si utilizza il Framework 4.5, ora ci sono le classi ZipArchive e ZipFile.
GalacticJello

Qualcuno ha usato DotNetZip ??
Hot Licks

Risposte:


85

Stai usando .NET 3.5? È possibile utilizzare la ZipPackageclasse e le classi correlate. È più che comprimere un elenco di file perché vuole un tipo MIME per ogni file che aggiungi. Potrebbe fare quello che vuoi.

Attualmente sto usando queste classi per un problema simile per archiviare diversi file correlati in un singolo file per il download. Utilizziamo un'estensione di file per associare il file di download alla nostra app desktop. Un piccolo problema che abbiamo riscontrato è che non è possibile utilizzare solo uno strumento di terze parti come 7-zip per creare i file zip perché il codice lato client non può aprirlo - ZipPackage aggiunge un file nascosto che descrive il tipo di contenuto di ogni file componente e non può aprire un file zip se manca quel file del tipo di contenuto.


1
Oh, come ti amo! Grazie Brian; ci hai appena risparmiato un sacco di mal di testa e un po 'di soldi.
Esteban Araya,

6
Nota che questo non funziona sempre al contrario. Alcuni file Zip non verranno reidratati utilizzando la classe ZipPackage. I file creati con ZipPackage andranno quindi bene.
Craig,

Si noti che ZipPackage non può essere aggiunto a un pacchetto zippato esistente.
ΩmegaMan,

Sospiro: "Il tipo o lo spazio dei nomi" Packaging "non esiste nello spazio dei nomi" System.IO ".
Hot Licks

2
(Rispondi al "sospiro" sopra: Apri "Riferimenti" e aggiungi (abbastanza illogicamente) "WindowsBase".)
Hot Licks

307

Come posso programmare (C #) ZIP un file (in Windows) senza usare librerie di terze parti?

Se si utilizza il Framework 4.5+, ora ci sono le classi ZipArchive e ZipFile .

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

Devi aggiungere riferimenti a:

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

Per .NET Core targeting net46, è necessario aggiungere dipendenze per

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

Esempio project.json:

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

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

Per .NET Core 2.0, è sufficiente aggiungere una semplice istruzione using:

  • utilizzando System.IO.Compression;

4
In che modo questo non ha ottenuto più voti? È l'unica risposta diretta.
Matt Cashatt,

12
Perché la domanda ha cinque anni, mentre questa risposta ha solo due mesi. Derp :-P
Riegardt Steyn,

3
@heliac è ancora la cosa Stackoverflow dovrebbe essere un repository di domande e risposte e nello spirito la migliore risposta dovrebbe essere in cima ... (accidenti, sapevo che non funziona)
Offler

5
Nel caso in cui aiuti qualcuno, il secondo argomento è la voce del file. Questo è il percorso in cui il file verrà estratto rispetto alla cartella di decompressione. In Windows 7, ho scoperto che se la voce del file è un percorso completo, ad esempio @ "D: \ Temp \ file1.pdf", l'estrattore di Windows nativo fallisce. È possibile riscontrare questo problema se si utilizzano semplicemente i nomi di file risultanti da Directory.GetFiles (). Meglio estrarre il nome del file usando Path.GetFileName () per l'argomento della voce del file.
Manish,

2
Non riesco a trovarlo in 4.5.2?
user3791372,

11

Ero nella stessa situazione, volendo .NET invece di una libreria di terze parti. Come un altro poster menzionato sopra, semplicemente usare la classe ZipPackage (introdotta in .NET 3.5) non è abbastanza. C'è un file aggiuntivo che DEVE essere incluso nell'archivio per far funzionare ZipPackage. Se questo file viene aggiunto, il pacchetto ZIP risultante può essere aperto direttamente da Esplora risorse - nessun problema.

Tutto quello che devi fare è aggiungere il file [Content_Types] .xml alla radice dell'archivio con un nodo "Predefinito" per ogni estensione che desideri includere. Una volta aggiunto, ho potuto sfogliare il pacchetto da Windows Explorer o decomprimere a livello di codice e leggere il suo contenuto.

Ulteriori informazioni sul file .xml [Content_Types] sono disponibili qui: http://msdn.microsoft.com/en-us/magazine/cc163372.aspx

Ecco un esempio del file [Content_Types] .xml (deve essere nominato esattamente):

<?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>

E il C # per la creazione di un file 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)); 
            } 
        } 
    } 

Nota:


12
Bel esempio, ma non crea un file ZIP. Decomprime un file esistente.
Matt Varblow,

9
    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");
    }

Come posso ottenere sourceFileName quando mi trovo all'interno di una webapi, ricevendo un HttpContext.Current.Request?
Olivertech,

Comprimi più file?
Kiquenet,

1

Sulla base della risposta di Simon McKenzie a questa domanda , suggerirei di utilizzare un paio di metodi come questo:

    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);
    }
}

0

Sembra che Windows potrebbe lasciarti fare questo ...

Sfortunatamente non penso che inizierai un processo separato se non vai a un componente di terze parti.


0

Aggiungi queste 4 funzioni al tuo progetto:

        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{}
        }
    }

E usali in questo modo:

foreach (string f in UnitZipList)
{
    AddFileToZip(zipFile, f);
    System.IO.File.Delete(f);
}
Remove_Content_Types_FromZip(zipFile);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.