如何在不使用 3rd-party API 的情况下用 C# 压缩文件?

Posted

技术标签:

【中文标题】如何在不使用 3rd-party API 的情况下用 C# 压缩文件?【英文标题】:How do I ZIP a file in C#, using no 3rd-party APIs? 【发布时间】:2010-10-30 17:56:16 【问题描述】:

我很确定这不是重复的,所以请耐心等待一分钟。

如何在不使用任何第三方库的情况下以编程方式 (C#) 压缩文件(在 Windows 中)?我需要一个本机 Windows 调用或类似的东西;我真的不喜欢开始一个过程的想法,但如果我绝对必须这样做,我会的。 PInovke 调用会好得多。

如果做不到这一点,让我告诉你我真正想要完成的工作:我需要能够让用户在一个请求中下载一组文档。关于如何实现这一点的任何想法?

【问题讨论】:

Compress Zip files with Windows Shell API and C# @Chesso:是的,来自 ASPX 页面。 几周前我在搜索相同内容时发现这个示例很有用:syntaxwarriors.com/2012/… 如果使用 4.5 框架,现在有 ZipArchive 和 ZipFile 类。 有人用过DotNetZip吗?? 【参考方案1】:

如何在不使用的情况下以编程方式 (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

对于面向 net46 的 .NET Core,您需要为

添加依赖项 System.IO.Compression System.IO.Compression.ZipFile

示例项目.json:

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

"frameworks": 
  "net46": 

对于 .NET Core 2.0,只需添加一个简单的 using 语句即可:

使用 System.IO.Compression;

【讨论】:

这怎么没有得到更多的支持?这是唯一直接的答案。 因为这个问题已有五年之久,而这个答案只有两个月之久。德普:-P @heliac 仍然 *** 的东西应该是一个问答存储库,并且在精神上最好的答案应该是最重要的......(该死,我知道这不起作用) 以防万一它对任何人有帮助,第二个参数是文件条目。这是相对于解压缩文件夹提取文件的路径。在 Windows 7 中,我发现如果文件条目是完整路径,例如 @"D:\Temp\file1.pdf",则本机 Windows 提取器会失败。如果您仅使用 Directory.GetFiles() 生成的文件名,您可能会遇到此问题。最好使用 Path.GetFileName() 作为文件条目参数来提取文件名。 我好像在 4.5.2 中找不到这个?【参考方案2】:

您使用的是 .NET 3.5 吗?您可以使用ZipPackage 类和相关类。它不仅仅是压缩文件列表,因为它需要您添加的每个文件的 MIME 类型。它可能会做你想做的事。

我目前正在使用这些类来解决类似的问题,将几个相关文件归档到一个文件中以供下载。我们使用文件扩展名将下载文件与我们的桌面应用程序相关联。我们遇到的一个小问题是不可能只使用 7-zip 之类的第三方工具来创建 zip 文件,因为客户端代码无法打开它——ZipPackage 添加了一个隐藏文件,描述了每个组件文件,如果缺少该内容类型文件,则无法打开 zip 文件。

【讨论】:

哦,我多么爱你!谢谢布赖恩;你刚刚为我们节省了很多头痛和一些美元。 请注意,这并不总是反过来工作。某些 Zip 文件不会使用 ZipPackage 类重新水化。使用 ZipPackage 制作的文件会很好。 请注意,ZipPackage 不能附加到现有的压缩包中。 叹息:“命名空间“System.IO”中不存在类型或命名空间“Packaging”。 (上面“叹息”的答案:打开“参考”并添加(不合逻辑地)“WindowsBase”。)【参考方案3】:

我也遇到了同样的情况,想要 .NET 而不是第三方库。正如上面提到的另一张海报,仅使用 ZipPackage 类(在 .NET 3.5 中引入)是不够的。为了使 ZipPackage 工作,必须在存档中包含一个附加文件。如果添加此文件,则可以直接从 Windows 资源管理器打开生成的 ZIP 包 - 没问题。

您所要做的就是将 [Content_Types].xml 文件添加到存档的根目录,并为您希望包含的每个文件扩展名添加一个“默认”节点。添加后,我可以从 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>

以及用于创建 ZIP 文件的 C#:

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

注意:

此代码使用 .NET 4.0 中的 Stream.CopyTo 方法 随着 .NET 4.5 中 ZipArchive 类的引入,这将变得更加简单:http://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive(v=vs.110).aspx

【讨论】:

不错的示例,但它不会创建 ZIP 文件。它解压缩现有文件。【参考方案4】:
    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");
    

【讨论】:

当我在 webapi 中接收 HttpContext.Current.Request 时如何获取 sourceFileName ? 压缩多个文件?【参考方案5】:

根据 Simon McKenzie 的回答 to this question,我建议使用以下两种方法:

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

【讨论】:

【参考方案6】:

看起来 Windows 可能只会让你做this...

不幸的是,除非您使用第三方组件,否则我认为您不会绕过启动单独的流程。

【讨论】:

【参考方案7】:

将这 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);

【讨论】:

以上是关于如何在不使用 3rd-party API 的情况下用 C# 压缩文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 API 的情况下查找 IP 地址的位置?

如何在不登录的情况下使用 google api 获取分析数据?

如何在不使用 Api 的情况下在 uitableview 中进行分页?

如何在不使用dotnet中的安全令牌(使用soap api)的情况下登录salesforce?

如何处理使用 3rd-party 的 restful store api 和 js mvc 框架的安全性?

如何在不使用任何 ID 的情况下从 spotify API 获取艺术家列表