Bagaimana cara ZIP file dalam C #, tanpa menggunakan API pihak ke-3?

175

Saya cukup yakin ini bukan duplikat jadi tahan dengan saya sebentar.

Bagaimana saya bisa secara programatik (C #) ZIP file (di Windows) tanpa menggunakan perpustakaan pihak ketiga? Saya perlu panggilan windows asli atau sesuatu seperti itu; Saya benar-benar tidak menyukai ide memulai suatu proses, tetapi saya akan melakukannya jika saya harus melakukannya. Panggilan PInovke akan jauh lebih baik.

Gagal itu, izinkan saya memberi tahu Anda apa yang sebenarnya ingin saya capai: Saya perlu kemampuan untuk membiarkan pengguna mengunduh kumpulan dokumen dalam satu permintaan. Ada ide tentang bagaimana mencapai ini?

Esteban Araya
sumber
1
@ Chesso: Ya, dari halaman ASPX.
Esteban Araya
1
Saya menemukan contoh ini berguna ketika saya sedang mencari hal yang sama beberapa minggu yang lalu: syntaxwarriors.com/2012/…
JensB
2
Jika menggunakan 4.5 Framework, sekarang ada kelas ZipArchive dan ZipFile.
GalacticJello
Adakah yang menggunakan DotNetZip ??
Hot Licks

Jawaban:

85

Apakah Anda menggunakan .NET 3.5? Anda bisa menggunakan ZipPackagekelas dan kelas terkait. Ini lebih dari sekadar membuka daftar file karena ingin jenis MIME untuk setiap file yang Anda tambahkan. Mungkin melakukan apa yang Anda inginkan.

Saat ini saya menggunakan kelas-kelas ini untuk masalah serupa untuk mengarsipkan beberapa file terkait ke dalam satu file untuk diunduh. Kami menggunakan ekstensi file untuk mengaitkan file unduhan dengan aplikasi desktop kami. Satu masalah kecil yang kami temui adalah bahwa tidak mungkin hanya menggunakan alat pihak ketiga seperti 7-zip untuk membuat file zip karena kode sisi klien tidak dapat membukanya - ZipPackage menambahkan file tersembunyi yang menggambarkan jenis konten dari setiap file komponen dan tidak dapat membuka file zip jika file tipe konten itu hilang.

Brian Ensink
sumber
1
Oh SO, betapa aku mencintaimu! Terima kasih Brian; Anda baru saja menyelamatkan kami dari sakit kepala dan sejumlah $$$.
Esteban Araya
6
Perhatikan bahwa ini tidak selalu bekerja secara terbalik. Beberapa file Zip tidak akan rehydrate menggunakan kelas ZipPackage. File yang dibuat dengan ZipPackage akan jadi Anda harus baik.
Craig
Perhatikan bahwa ZipPackage tidak dapat menambahkan paket zip yang ada.
ΩmegaMan
Sigh: "Tipe atau namespace" Pembungkus "tidak ada di namespace" System.IO ".
Hot Licks
2
(Jawab untuk "desahan" di atas: Buka "Referensi" dan tambahkan (cukup tidak masuk akal) "WindowsBase".)
Hot Licks
307

Bagaimana saya bisa secara programatik (C #) ZIP file (di Windows) tanpa menggunakan perpustakaan pihak ketiga?

Jika menggunakan 4.5+ Framework, sekarang ada kelas ZipArchive dan ZipFile .

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

Anda perlu menambahkan referensi ke:

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

Untuk .NET Core menargetkan net46, Anda perlu menambahkan dependensi untuk

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

Contoh project.json:

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

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

Untuk .NET Core 2.0, hanya perlu menambahkan pernyataan penggunaan sederhana:

  • menggunakan System.IO.Compression;
GalacticJello
sumber
4
Bagaimana ini tidak mendapatkan lebih banyak upvotes? Itu satu-satunya jawaban langsung.
Matt Cashatt
12
Karena pertanyaannya lima tahun, sedangkan jawaban ini baru dua bulan. Derp :-P
Heliac
3
@heliac masih Stackoverflow thingie harus menjadi repositori pertanyaan dan jawaban dan dalam semangat jawaban terbaik seharusnya ada di atas ... (sial, aku tahu ini tidak bekerja)
Offler
5
Kalau-kalau itu membantu siapa pun, argumen kedua adalah entri file. Ini adalah path tempat file akan diekstraksi relatif terhadap folder unzip. Di Windows 7, saya menemukan bahwa jika entri file adalah path lengkap, misalnya, @ "D: \ Temp \ file1.pdf", extractor Windows asli gagal. Anda dapat mengalami masalah ini jika Anda hanya menggunakan nama file yang dihasilkan dari Directory.GetFiles (). Terbaik untuk mengekstrak nama file menggunakan Path.GetFileName () untuk argumen entri file.
Manish
2
Saya sepertinya tidak dapat menemukan ini di 4.5.2?
user3791372
11

Saya berada dalam situasi yang sama, ingin. NET, bukan perpustakaan pihak ketiga. Seperti poster lain yang disebutkan di atas, cukup menggunakan kelas ZipPackage (diperkenalkan pada .NET 3.5) tidak cukup. Ada file tambahan yang HARUS disertakan dalam arsip agar ZipPackage berfungsi. Jika file ini ditambahkan, maka paket ZIP yang dihasilkan dapat dibuka langsung dari Windows Explorer - tidak ada masalah.

Yang harus Anda lakukan adalah menambahkan file [Content_Types] .xml ke root arsip dengan simpul "Default" untuk setiap ekstensi file yang ingin Anda sertakan. Setelah ditambahkan, saya dapat menelusuri paket dari Windows Explorer atau secara program mendekompresi dan membaca isinya.

Informasi lebih lanjut tentang file [Content_Types] .xml dapat ditemukan di sini: http://msdn.microsoft.com/en-us/magazine/cc163372.aspx

Berikut ini contoh file [Content_Types] .xml (harus diberi nama persis):

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

Dan C # untuk membuat 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)); 
            } 
        } 
    } 

catatan:

Joshua
sumber
12
Sampel yang bagus, tetapi tidak membuat file ZIP. Ini membuka ritsleting file yang ada.
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");
    }
BERKEDIP
sumber
Bagaimana saya bisa mendapatkan sourceFileName ketika saya di dalam webapi, menerima HttpContext.Current.Request?
Olivertech
Kompres lebih dari satu file?
Kiquenet
1

Berdasarkan jawaban Simon McKenzie untuk pertanyaan ini , saya sarankan menggunakan sepasang metode seperti ini:

    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);
    }
}
mccdyl001
sumber
0

Sepertinya Windows mungkin membiarkan Anda melakukan ini ...

Sayangnya saya tidak berpikir Anda akan memulai memulai proses terpisah kecuali jika Anda pergi ke komponen pihak ketiga.

Dave Swersky
sumber
0

Tambahkan 4 fungsi ini ke proyek Anda:

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

Dan gunakan seperti ini:

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