ฉันจะ ZIP ไฟล์ใน C # โดยไม่ใช้ API ของบุคคลที่สามได้อย่างไร


175

ฉันค่อนข้างแน่ใจว่านี่ไม่ใช่สิ่งที่ซ้ำกันดังนั้นโปรดอดทนกับฉันสักครู่

ฉันจะเขียนโปรแกรม (C #) บีบอัดไฟล์ (ใน Windows) โดยไม่ต้องใช้ห้องสมุดบุคคลที่สามได้อย่างไร ฉันต้องการการโทรแบบ windows พื้นเมืองหรืออะไรแบบนั้น ฉันไม่ชอบความคิดที่จะเริ่มต้นกระบวนการจริงๆ แต่ฉันจะทำถ้าฉันต้องทำอย่างแน่นอน การเรียก PInovke ดีกว่ามาก

ให้ฉันบอกคุณว่าฉันพยายามทำอะไรสำเร็จจริง ๆ : ฉันต้องการความสามารถในการอนุญาตให้ผู้ใช้ดาวน์โหลดชุดเอกสารในคำขอเดียว ความคิดใด ๆ เกี่ยวกับวิธีการทำสิ่งนี้?



1
@Chesso: ใช่จากหน้า ASPX
Esteban Araya

1
ฉันพบตัวอย่างนี้มีประโยชน์เมื่อฉันค้นหาสิ่งเดียวกันเมื่อสองสามสัปดาห์ก่อน: syntaxwarriors.com/2012/…
JensB

2
หากใช้ 4.5 Framework ตอนนี้จะมีคลาส ZipArchive และ ZipFile
GalacticJello

ใครใช้ DotNetZip
เลียร้อน

คำตอบ:


85

คุณใช้. NET 3.5 หรือไม่ คุณสามารถใช้ZipPackageคลาสและคลาสที่เกี่ยวข้องได้ มันมากกว่าแค่การซิปรายการไฟล์เพราะมันต้องการประเภท MIME สำหรับแต่ละไฟล์ที่คุณเพิ่ม มันอาจทำสิ่งที่คุณต้องการ

ขณะนี้ฉันกำลังใช้คลาสเหล่านี้สำหรับปัญหาที่คล้ายกันเพื่อเก็บไฟล์ที่เกี่ยวข้องหลายไฟล์ไว้ในไฟล์เดียวเพื่อดาวน์โหลด เราใช้นามสกุลไฟล์เพื่อเชื่อมโยงไฟล์ดาวน์โหลดกับแอพเดสก์ท็อปของเรา ปัญหาเล็ก ๆ หนึ่งที่เราพบคือมันเป็นไปไม่ได้ที่จะใช้เครื่องมือของบุคคลที่สามเช่น 7-zip เพื่อสร้างไฟล์ zip เพราะรหัสฝั่งไคลเอ็นต์ไม่สามารถเปิดได้ - ZipPackage เพิ่มไฟล์ที่ซ่อนอธิบายประเภทเนื้อหาของ แต่ละไฟล์คอมโพเนนต์และไม่สามารถเปิดไฟล์ zip หากไฟล์ประเภทเนื้อหานั้นหายไป


1
โอ้โหฉันรักคุณ! ขอบคุณไบรอัน; yuo เพิ่งช่วยเราปวดหัวและ $$$ บางส่วน
Esteban Araya

6
โปรดทราบว่านี่จะไม่ทำงานในสิ่งที่ตรงกันข้าม ไฟล์ซิปบางไฟล์จะไม่กลับมาใช้งานโดยใช้คลาส ZipPackage ไฟล์ที่สร้างด้วย ZipPackage จะช่วยให้คุณดีขึ้น
Craig

โปรดทราบว่า ZipPackage ไม่สามารถผนวกเข้ากับแพ็คเกจซิปที่มีอยู่
ΩmegaMan

ถอนหายใจ: "ประเภทหรือเนมสเปซ" บรรจุภัณฑ์ "ไม่มีอยู่ในเนมสเปซ" System.IO "
Hot Licks

2
(ตอบไปที่ "ถอนหายใจ" ด้านบน: เปิด "การอ้างอิง" และเพิ่ม (ไม่มีเหตุผลพอ) "WindowsBase".)
Hot Licks

307

ฉันจะเขียนโปรแกรม (C #) บีบอัดไฟล์ (ใน Windows) โดยไม่ต้องใช้ห้องสมุดบุคคลที่สามได้อย่างไร

ถ้าใช้ 4.5 + กรอบมีตอนนี้เป็นZipArchiveและZipFileชั้นเรียน

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

คุณต้องเพิ่มการอ้างอิงถึง:

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

สำหรับ. NET Core ที่กำหนดเป้าหมาย net46 คุณจะต้องเพิ่มการพึ่งพา

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

ตัวอย่าง project.json:

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

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

สำหรับ. NET Core 2.0 เพียงแค่เพิ่มคำสั่งการใช้ง่าย ๆ คือทั้งหมดที่ต้องการ:

  • ใช้ System.IO.Compression

4
สิ่งนี้มีวิธีเพิ่มจำนวนผู้ติดตามอย่างไร มันเป็นคำตอบโดยตรงเท่านั้น
Matt Cashatt

12
เพราะคำถามมีอายุห้าขวบในขณะที่คำตอบนี้มีอายุเพียงสองเดือน Derp :-P
Heliac

3
@heliac ยังคง thingie Stackoverflow ควรจะเป็นคำถามและคำตอบที่เก็บและจิตวิญญาณตอบที่ดีที่สุด shoudl จะอยู่ด้านบน ... (แช่งฉันรู้ว่าเรื่องนี้ไม่ทำงาน)
Offler

5
ในกรณีที่มันช่วยใครก็ได้อาร์กิวเมนต์ที่สองคือรายการไฟล์ นี่คือพา ธ ที่จะแตกไฟล์ที่สัมพันธ์กับโฟลเดอร์แตกไฟล์ ใน Windows 7 ฉันพบว่าหากรายการไฟล์เป็นเส้นทางแบบเต็มเช่น @ "D: \ Temp \ file1.pdf" ตัวแยกข้อมูลดั้งเดิมของ Windows จะล้มเหลว คุณอาจพบปัญหานี้หากคุณใช้ชื่อไฟล์ที่เป็นผลมาจาก Directory.GetFiles () ดีที่สุดในการแยกชื่อไฟล์โดยใช้ Path.GetFileName () สำหรับอาร์กิวเมนต์รายการไฟล์
Manish

2
ฉันดูเหมือนจะไม่พบสิ่งนี้ใน 4.5.2?
3791372

11

ฉันอยู่ในสถานการณ์เดียวกันต้องการ. NET แทนที่จะเป็นห้องสมุดบุคคลที่สาม ดังที่ผู้โพสต์อีกคนหนึ่งกล่าวไว้ข้างต้นเพียงแค่ใช้คลาส ZipPackage (แนะนำใน. NET 3.5) ก็ยังไม่เพียงพอ มีไฟล์เพิ่มเติมที่ต้องรวมอยู่ในไฟล์เก็บถาวรเพื่อให้ ZipPackage ทำงานได้ หากมีการเพิ่มไฟล์นี้แพคเกจ ZIP ที่เป็นผลลัพธ์สามารถเปิดได้โดยตรงจาก Windows Explorer - ไม่มีปัญหา

สิ่งที่คุณต้องทำคือเพิ่มไฟล์ [Content_Types] .xml ในรากของไฟล์เก็บถาวรด้วยโหนด "Default" สำหรับทุกนามสกุลไฟล์ที่คุณต้องการรวม เมื่อเพิ่มฉันสามารถเรียกดูแพคเกจจาก Windows Explorer หรือคลายการบีบอัดทางโปรแกรมและอ่านเนื้อหา

ข้อมูลเพิ่มเติมเกี่ยวกับไฟล์ [Content_Types] .xml สามารถดูได้ที่นี่: http://msdn.microsoft.com/en-us/magazine/cc163372.aspx

นี่คือตัวอย่างของไฟล์ [Content_Types] .xml (ต้องตั้งชื่ออย่างแน่นอน):

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

และ C # สำหรับการสร้างไฟล์ 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)); 
            } 
        } 
    } 

บันทึก:


12
ตัวอย่างที่ดี แต่มันไม่ได้สร้างไฟล์ ZIP มันคลายซิปไฟล์ที่มีอยู่
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");
    }

ฉันจะรับ sourceFileName ได้อย่างไรเมื่อฉันอยู่ใน webapi เพื่อรับ HttpContext.Current.Request
Olivertech

บีบอัดมากกว่าหนึ่งไฟล์?
Kiquenet

1

จากคำตอบของ Simon McKenzie สำหรับคำถามนี้ฉันขอแนะนำให้ใช้วิธีการดังนี้:

    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

ดูเหมือนว่า Windows จะอนุญาตให้คุณทำเช่นนี้ ...

น่าเสียดายที่ฉันไม่คิดว่าคุณจะเริ่มต้นกระบวนการที่แยกต่างหากเว้นแต่คุณจะไปที่องค์ประกอบของบุคคลที่สาม


0

เพิ่ม 4 ฟังก์ชันเหล่านี้ในโครงการของคุณ:

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

และใช้พวกเขาเช่นนี้

foreach (string f in UnitZipList)
{
    AddFileToZip(zipFile, f);
    System.IO.File.Delete(f);
}
Remove_Content_Types_FromZip(zipFile);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.