C#压缩库SharpZipLib的应用
SharpZipLib是一个开源的C#压缩解压库,应用非常广泛。就像用ADO.NET操作数据库要打开连接、执行命令、关闭连接等多个步骤一样,用SharpZipLib进行压缩和解压也需要多个步骤。除了初学者会用原始的方法做一步一步完成,实际开发的时候都会进行更易用的封装。这里分享我对SharpZipLib的封装,支持对文件和字节进行压缩和解压操作,支持多个文件和文件夹压缩,支持设置备注和密码。(文章源网址:http://www.cnblogs.com/conexpress/p/SharpZipClass.html)
SharpZipLib的官方地址是:http://icsharpcode.github.io/SharpZipLib/,实际使用可以通过NuGet获取,在NuGet的地址是:http://www.nuget.org/packages/SharpZipLib/
在Visual Studio中可以通过NuGet程序包管理控制台输入命令PM> Install-Package SharpZipLib或者用NuGet管理界面搜索并安装。
我把使用SharpZipLib的方法卸载一个类里面,作为静态方法调用,免去创建类的麻烦。其中核心压缩和解压文件的方法代码如下:
1 /// <summary> 2 /// 压缩多个文件/文件夹 3 /// </summary> 4 /// <param name="sourceList">源文件/文件夹路径列表</param> 5 /// <param name="zipFilePath">压缩文件路径</param> 6 /// <param name="comment">注释信息</param> 7 /// <param name="password">压缩密码</param> 8 /// <param name="compressionLevel">压缩等级,范围从0到9,可选,默认为6</param> 9 /// <returns></returns> 10 public static bool CompressFile(IEnumerable<string> sourceList, string zipFilePath, 11 string comment = null, string password = null, int compressionLevel = 6) 12 { 13 bool result = false; 14 15 try 16 { 17 //检测目标文件所属的文件夹是否存在,如果不存在则建立 18 string zipFileDirectory = Path.GetDirectoryName(zipFilePath); 19 if (!Directory.Exists(zipFileDirectory)) 20 { 21 Directory.CreateDirectory(zipFileDirectory); 22 } 23 24 Dictionary<string, string> dictionaryList = PrepareFileSystementities(sourceList); 25 26 using (ZipOutputStream zipStream = new ZipOutputStream(File.Create(zipFilePath))) 27 { 28 zipStream.Password = password;//设置密码 29 zipStream.SetComment(comment);//添加注释 30 zipStream.SetLevel(CheckCompressionLevel(compressionLevel));//设置压缩等级 31 32 foreach (string key in dictionaryList.Keys)//从字典取文件添加到压缩文件 33 { 34 if (File.Exists(key))//判断是文件还是文件夹 35 { 36 FileInfo fileItem = new FileInfo(key); 37 38 using (FileStream readStream = fileItem.Open(FileMode.Open, 39 FileAccess.Read, FileShare.Read)) 40 { 41 ZipEntry zipEntry = new ZipEntry(dictionaryList[key]); 42 zipEntry.DateTime = fileItem.LastWriteTime; 43 zipEntry.Size = readStream.Length; 44 zipStream.PutNextEntry(zipEntry); 45 int readLength = 0; 46 byte[] buffer = new byte[BufferSize]; 47 48 do 49 { 50 readLength = readStream.Read(buffer, 0, BufferSize); 51 zipStream.Write(buffer, 0, readLength); 52 } while (readLength == BufferSize); 53 54 readStream.Close(); 55 } 56 } 57 else//对文件夹的处理 58 { 59 ZipEntry zipEntry = new ZipEntry(dictionaryList[key] + "/"); 60 zipStream.PutNextEntry(zipEntry); 61 } 62 } 63 64 zipStream.Flush(); 65 zipStream.Finish(); 66 zipStream.Close(); 67 } 68 69 result = true; 70 } 71 catch (System.Exception ex) 72 { 73 throw new Exception("压缩文件失败", ex); 74 } 75 76 return result; 77 } 78 79 /// <summary> 80 /// 解压文件到指定文件夹 81 /// </summary> 82 /// <param name="sourceFile">压缩文件</param> 83 /// <param name="destinationDirectory">目标文件夹,如果为空则解压到当前文件夹下</param> 84 /// <param name="password">密码</param> 85 /// <returns></returns> 86 public static bool DecomparessFile(string sourceFile, string destinationDirectory = null, string password = null) 87 { 88 bool result = false; 89 90 if (!File.Exists(sourceFile)) 91 { 92 throw new FileNotFoundException("要解压的文件不存在", sourceFile); 93 } 94 95 if (string.IsNullOrWhiteSpace(destinationDirectory)) 96 { 97 destinationDirectory = Path.GetDirectoryName(sourceFile); 98 } 99 100 try 101 { 102 if (!Directory.Exists(destinationDirectory)) 103 { 104 Directory.CreateDirectory(destinationDirectory); 105 } 106 107 using (ZipInputStream zipStream = new ZipInputStream(File.Open(sourceFile, FileMode.Open, 108 FileAccess.Read, FileShare.Read))) 109 { 110 zipStream.Password = password; 111 ZipEntry zipEntry = zipStream.GetNextEntry(); 112 113 while (zipEntry != null) 114 { 115 if (zipEntry.IsDirectory)//如果是文件夹则创建 116 { 117 Directory.CreateDirectory(Path.Combine(destinationDirectory, 118 Path.GetDirectoryName(zipEntry.Name))); 119 } 120 else 121 { 122 string fileName = Path.GetFileName(zipEntry.Name); 123 if (!string.IsNullOrEmpty(fileName) && fileName.Trim().Length > 0) 124 { 125 FileInfo fileItem = new FileInfo(Path.Combine(destinationDirectory, zipEntry.Name)); 126 using (FileStream writeStream = fileItem.Create()) 127 { 128 byte[] buffer = new byte[BufferSize]; 129 int readLength = 0; 130 131 do 132 { 133 readLength = zipStream.Read(buffer, 0, BufferSize); 134 writeStream.Write(buffer, 0, readLength); 135 } while (readLength == BufferSize); 136 137 writeStream.Flush(); 138 writeStream.Close(); 139 } 140 fileItem.LastWriteTime = zipEntry.DateTime; 141 } 142 } 143 zipEntry = zipStream.GetNextEntry();//获取下一个文件 144 } 145 146 zipStream.Close(); 147 } 148 result = true; 149 } 150 catch (System.Exception ex) 151 { 152 throw new Exception("文件解压发生错误", ex); 153 } 154 155 return result; 156 }
压缩方法CompressFile中sourceList是路径数组,支持文件夹和文件的路径。当遇到文件夹时会自动将下级文件和文件夹包含,并会检测重复项。文件路径处理的方法如下:
1 /// <summary> 2 /// 为压缩准备文件系统对象 3 /// </summary> 4 /// <param name="sourceFileEntityPathList"></param> 5 /// <returns></returns> 6 private static Dictionary<string, string> PrepareFileSystementities(IEnumerable<string> sourceFileEntityPathList) 7 { 8 Dictionary<string, string> fileEntityDictionary = new Dictionary<string, string>();//文件字典 9 string parentDirectoryPath = ""; 10 foreach (string fileEntityPath in sourceFileEntityPathList) 11 { 12 string path = fileEntityPath; 13 //保证传入的文件夹也被压缩进文件 14 if (path.EndsWith(@"\")) 15 { 16 path = path.Remove(path.LastIndexOf(@"\")); 17 } 18 19 parentDirectoryPath = Path.GetDirectoryName(path) + @"\"; 20 21 if (parentDirectoryPath.EndsWith(@":\\"))//防止根目录下把盘符压入的错误 22 { 23 parentDirectoryPath = parentDirectoryPath.Replace(@"\\", @"\"); 24 } 25 26 //获取目录中所有的文件系统对象 27 Dictionary<string, string> subDictionary = GetAllFileSystemEntities(path, parentDirectoryPath); 28 29 //将文件系统对象添加到总的文件字典中 30 foreach (string key in subDictionary.Keys) 31 { 32 if (!fileEntityDictionary.ContainsKey(key))//检测重复项 33 { 34 fileEntityDictionary.Add(key, subDictionary[key]); 35 } 36 } 37 } 38 return fileEntityDictionary; 39 } 40 41 /// <summary> 42 /// 获取所有文件系统对象 43 /// </summary> 44 /// <param name="source">源路径</param> 45 /// <param name="topDirectory">顶级文件夹</param> 46 /// <returns>字典中Key为完整路径,Value为文件(夹)名称</returns> 47 private static Dictionary<string, string> GetAllFileSystemEntities(string source, string topDirectory) 48 { 49 Dictionary<string, string> entitiesDictionary = new Dictionary<string, string>(); 50 entitiesDictionary.Add(source, source.Replace(topDirectory, "")); 51 52 if (Directory.Exists(source)) 53 { 54 //一次性获取下级所有目录,避免递归 55 string[] directories = Directory.GetDirectories(source, "*.*", SearchOption.AllDirectories); 56 foreach (string directory in directories) 57 { 58 entitiesDictionary.Add(directory, directory.Replace(topDirectory, "")); 59 } 60 61 string[] files = Directory.GetFiles(source, "*.*", SearchOption.AllDirectories); 62 foreach (string file in files) 63 { 64 entitiesDictionary.Add(file, file.Replace(topDirectory, "")); 65 } 66 } 67 68 return entitiesDictionary; 69 }
除了支持文件和文件夹压缩解压,还提供了对字节的压缩解压方法:
1 /// <summary> 2 /// 压缩字节数组 3 /// </summary> 4 /// <param name="sourceBytes">源字节数组</param> 5 /// <param name="compressionLevel">压缩等级</param> 6 /// <param name="password">密码</param> 7 /// <returns>压缩后的字节数组</returns> 8 public static byte[] CompressBytes(byte[] sourceBytes, string password = null, int compressionLevel = 6) 9 { 10 byte[] result = new byte[] { }; 11 12 if (sourceBytes.Length > 0) 13 { 14 try 15 { 16 using (MemoryStream tempStream = new MemoryStream()) 17 { 18 using (MemoryStream readStream = new MemoryStream(sourceBytes)) 19 { 20 using (ZipOutputStream zipStream = new ZipOutputStream(tempStream)) 21 { 22 zipStream.Password = password;//设置密码 23 zipStream.SetLevel(CheckCompressionLevel(compressionLevel));//设置压缩等级 24 25 ZipEntry zipEntry = new ZipEntry("ZipBytes"); 26 zipEntry.DateTime = DateTime.Now; 27 zipEntry.Size = sourceBytes.Length; 28 zipStream.PutNextEntry(zipEntry); 29 int readLength = 0; 30 byte[] buffer = new byte[BufferSize]; 31 32 do 33 { 34 readLength = readStream.Read(buffer, 0, BufferSize); 35 zipStream.Write(buffer, 0, readLength); 36 } while (readLength == BufferSize); 37 38 readStream.Close(); 39 zipStream.Flush(); 40 zipStream.Finish(); 41 result = tempStream.ToArray(); 42 zipStream.Close(); 43 } 44 } 45 } 46 } 47 catch (System.Exception ex) 48 { 49 throw new Exception("压缩字节数组发生错误", ex); 50 } 51 } 52 53 return result; 54 } 55 56 /// <summary> 57 /// 解压字节数组 58 /// </summary> 59 /// <param name="sourceBytes">源字节数组</param> 60 /// <param name="password">密码</param> 61 /// <returns>解压后的字节数组</returns> 62 public static byte[] DecompressBytes(byte[] sourceBytes, string password = null) 63 { 64 byte[] result = new byte[] { }; 65 66 if (sourceBytes.Length > 0) 67 { 68 try 69 { 70 using (MemoryStream tempStream = new MemoryStream(sourceBytes)) 71 { 72 using (MemoryStream writeStream = new MemoryStream()) 73 { 74 using (ZipInputStream zipStream = new ZipInputStream(tempStream)) 75 { 76 zipStream.Password = password; 77 ZipEntry zipEntry = zipStream.GetNextEntry(); 78 79 if (zipEntry != null) 80 { 81 byte[] buffer = new byte[BufferSize]; 82 int readLength = 0; 83 84 do 85 { 86 readLength = zipStream.Read(buffer, 0, BufferSize); 87 writeStream.Write(buffer, 0, readLength); 88 } while (readLength == BufferSize); 89 90 writeStream.Flush(); 91 result = writeStream.ToArray(); 92 writeStream.Close(); 93 } 94 zipStream.Close(); 95 } 96 } 97 } 98 } 99 catch (System.Exception ex) 100 { 101 throw new Exception("解压字节数组发生错误", ex); 102 } 103 } 104 return result; 105 }
为了测试该类,我写了一个WinForm程序,界面如下:
可以将文件和文件夹拖放到压缩列表中,选中压缩列表中的对象可以按Delete键进行删除。文件解压区域也可以将要解压的文件拖放到文件路径输入文本框。实际测试可以正确压缩和解压,包括设置密码,压缩和解压操作和WinRAR完全兼容。如果您在代码中发现问题或有可以优化完善的地方,欢迎指出,谢谢!
程序源代码下载:https://files.cnblogs.com/files/conexpress/SharpZipTest.zip
Author:Alex Leo
Email:conexpress@qq.com
Blog:http://conexpress.cnblogs.com/