Làm cách nào để ZIP tệp trong C #, không sử dụng API của bên thứ 3?


175

Tôi khá chắc chắn rằng đây không phải là một bản sao vì vậy hãy chịu đựng tôi chỉ trong một phút.

Làm cách nào tôi có thể lập trình (C #) ZIP một tệp (trong Windows) mà không cần sử dụng bất kỳ thư viện bên thứ ba nào? Tôi cần một cuộc gọi cửa sổ riêng hoặc một cái gì đó tương tự; Tôi thực sự không thích ý tưởng bắt đầu một quá trình, nhưng tôi sẽ làm nếu tôi hoàn toàn phải làm. Một cuộc gọi PInovke sẽ tốt hơn nhiều.

Thất bại trong điều đó, hãy để tôi nói cho bạn biết những gì tôi thực sự đang cố gắng thực hiện: Tôi cần khả năng cho phép người dùng tải xuống bộ sưu tập tài liệu trong một yêu cầu. Bất kỳ ý tưởng về làm thế nào để thực hiện điều này?



1
@Chesso: Vâng, từ một trang ASPX.
Esteban Araya

1
Tôi tìm thấy ví dụ này hữu ích khi tôi đang tìm kiếm điều tương tự một vài tuần trước: syntaxwarriors.com/2012/...
JensB

2
Nếu sử dụng Khung 4.5, bây giờ có các lớp ZipArchive và ZipFile.
GalacticJello

Có ai dùng DotNetZip không ??
Hot Licks

Câu trả lời:


85

Bạn đang sử dụng .NET 3.5? Bạn có thể sử dụng ZipPackagelớp và các lớp liên quan. Nó không chỉ đơn giản là nén danh sách tệp vì nó muốn loại MIME cho mỗi tệp bạn thêm. Nó có thể làm những gì bạn muốn.

Tôi hiện đang sử dụng các lớp này cho một vấn đề tương tự để lưu trữ một số tệp có liên quan vào một tệp để tải xuống. Chúng tôi sử dụng tiện ích mở rộng tệp để liên kết tệp tải xuống với ứng dụng máy tính để bàn của chúng tôi. Một vấn đề nhỏ mà chúng tôi gặp phải là không thể chỉ sử dụng công cụ của bên thứ ba như 7-zip để tạo tệp zip vì mã phía máy khách không thể mở được - ZipPackage thêm một tệp ẩn mô tả loại nội dung của mỗi tệp thành phần và không thể mở tệp zip nếu tệp loại nội dung đó bị thiếu.


1
Ôi, em yêu anh làm sao! Cảm ơn Brian; yuo chỉ cứu chúng tôi rất nhiều đau đầu và một số $$$.
Esteban Araya

6
Lưu ý rằng điều này không phải lúc nào cũng hoạt động ngược lại. Một số tệp Zip sẽ không bù nước bằng lớp ZipPackage. Các tập tin được tạo bằng ZipPackage sẽ giúp bạn trở nên tốt hơn.
Craig

Lưu ý rằng ZipPackage không thể nối thêm gói đã nén.
ΩmegaMan

Thở dài: "Loại hoặc không gian tên" Bao bì "không tồn tại trong không gian tên" System.IO ".
Hot Licks

2
(Trả lời "tiếng thở dài" ở trên: Mở "Tài liệu tham khảo" và thêm (đủ logic) "WindowsBase".)
Hot Licks

307

Làm cách nào tôi có thể lập trình (C #) ZIP một tệp (trong Windows) mà không cần sử dụng bất kỳ thư viện bên thứ ba nào?

Nếu sử dụng Khung 4.5+, giờ đây đã có các lớp ZipArchiveZipFile .

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

Bạn cần thêm tài liệu tham khảo vào:

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

Đối với .NET Core nhắm mục tiêu net46, bạn cần thêm phụ thuộc cho

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

Ví dụ project.json:

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

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

Đối với .NET Core 2.0, chỉ cần thêm một câu lệnh sử dụng đơn giản là tất cả những gì cần thiết:

  • sử dụng System.IO.Compression;

4
Làm thế nào có điều này không nhận được nhiều upvote? Đó là câu trả lời trực tiếp duy nhất.
Matt Cashatt

12
Bởi vì câu hỏi là năm tuổi, trong khi câu trả lời này chỉ hai tháng tuổi. Derp :-P
Heliac

3
@rciac vẫn là kho lưu trữ Stackoverflow nên là kho lưu trữ câu hỏi và câu trả lời và theo tinh thần, câu trả lời hay nhất sẽ được đặt lên hàng đầu ... (chết tiệt, tôi biết điều này không hoạt động)
Offler 18/214

5
Chỉ trong trường hợp nó giúp được bất cứ ai, đối số thứ hai là mục nhập tệp. Đây là đường dẫn mà tệp sẽ được trích xuất liên quan đến thư mục giải nén. Trong Windows 7, tôi thấy rằng nếu mục nhập tệp là một đường dẫn đầy đủ, ví dụ: @ "D: \ Temp \ file1.pdf", trình trích xuất Windows gốc sẽ thất bại. Bạn có thể gặp phải vấn đề này nếu bạn chỉ cần sử dụng tên tệp kết quả từ Directory.GetFiles (). Tốt nhất để trích xuất tên tệp bằng Path.GetFileName () cho đối số nhập tệp.
Manish

2
Tôi dường như không thể tìm thấy điều này trong 4.5.2?
dùng3791372

11

Tôi đã ở trong tình trạng tương tự, muốn .NET thay vì thư viện của bên thứ ba. Như một poster khác đã đề cập ở trên, chỉ cần sử dụng lớp ZipPackage (được giới thiệu trong .NET 3.5) là không đủ. Có một tệp bổ sung PHẢI được đưa vào kho lưu trữ để ZipPackage hoạt động. Nếu tệp này được thêm vào, thì gói ZIP kết quả có thể được mở trực tiếp từ Windows Explorer - không có vấn đề gì.

Tất cả những gì bạn phải làm là thêm tệp [Content_Types] .xml vào thư mục gốc của tệp lưu trữ với nút "Mặc định" cho mọi tiện ích mở rộng tệp bạn muốn đưa vào. Sau khi thêm, tôi có thể duyệt gói từ Windows Explorer hoặc giải nén theo chương trình và đọc nội dung của nó.

Thông tin thêm về tệp [Content_Types] .xml có thể được tìm thấy tại đây: http://msdn.microsoft.com/en-us/magazine/cc163372.aspx

Dưới đây là mẫu của tệp [Content_Types] .xml (phải được đặt tên chính xác):

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

Và C # để tạo tệp 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)); 
            } 
        } 
    } 

Ghi chú:

  • Mã này sử dụng phương thức Stream.CopyTo trong .NET 4.0
  • Điều này sẽ trở nên đơn giản hơn nhiều với việc giới thiệu lớp ZipArchive trong .NET 4.5: http://msdn.microsoft.com/en-us/l Library / system.io.compression.ziparchive (v = vs.110) .aspx

12
Mẫu đẹp, nhưng nó không tạo tệp ZIP. Nó giải nén một tập tin hiện có.
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");
    }

Làm cách nào tôi có thể nhận được sourceFileName khi tôi ở trong một webapi, nhận được một HTTPContext.Cản.Request?
OliverTech

Nén thêm một tập tin?
Kiquenet

1

Dựa trên câu trả lời của Simon McKenzie cho câu hỏi này , tôi khuyên bạn nên sử dụng một cặp phương pháp như thế này:

    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

Có vẻ như Windows có thể cho phép bạn làm điều này ...

Thật không may, tôi không nghĩ rằng bạn sẽ bắt đầu một quy trình riêng trừ khi bạn đi đến một thành phần bên thứ ba.


0

Thêm 4 chức năng này vào dự án của bạn:

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

Và sử dụng chúng như thế này:

foreach (string f in UnitZipList)
{
    AddFileToZip(zipFile, f);
    System.IO.File.Delete(f);
}
Remove_Content_Types_FromZip(zipFile);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.