最近在做的一个需求是这样的:将导出的所有文件压缩为一个压缩文件,导入的时候再解压该压缩文件并读取文件内容。要解决这个问题,需要做如下几步操作方能实现:
1. 将导出的所有文件生成到本地内存的一个位置为临时文件夹
2. 将该文件夹压缩为一个压缩文件到用户指定路径
3. 删除该临时文件夹
在这个需求过程中涉及到如下几个操作:文件夹压缩为一个压缩文件,删除临时文件夹,解压压缩文件。
1 文件夹压缩为一个压缩文件
/// <summary> /// 压缩所有的文件 /// </summary> /// <param name="filesPath">要压缩的文件路径</param> /// <param name="zipFilePath">压缩完成后的压缩文件路径</param> public static void CreateZipFile(string filesPath, string zipFilePath) { if (!Directory.Exists(filesPath)) { return; } var stream = new ZipOutputStream(File.Create(zipFilePath)); stream.SetLevel(0); // 压缩级别 0-9 var buffer = new byte[4096]; //缓冲区大小 var fileNames = Directory.GetFiles(filesPath, "*.*", SearchOption.AllDirectories); foreach (var file in fileNames) { var entry = new ZipEntry(file.Replace(filesPath, "")) { DateTime = DateTime.Now }; stream.PutNextEntry(entry); using (var fs = File.OpenRead(file)) { int sourceBytes; do { sourceBytes = fs.Read(buffer, 0, buffer.Length); stream.Write(buffer, 0, sourceBytes); } while (sourceBytes > 0); } } stream.Finish(); stream.Close(); }
2 删除临时文件夹
/// <summary> /// 直接删除指定目录下的所有文件及文件夹(保留目录) /// </summary> /// <param name="folderPath"></param> public static void DeleteDir(string folderPath) { try { //去除文件夹和子文件的只读属性 //去除文件夹的只读属性 System.IO.DirectoryInfo fileInfo = new DirectoryInfo(folderPath); fileInfo.Attributes = FileAttributes.Normal & FileAttributes.Directory; //去除文件的只读属性 System.IO.File.SetAttributes(folderPath, System.IO.FileAttributes.Normal); //判断文件夹是否还存在 if (Directory.Exists(folderPath)) { foreach (string f in Directory.GetFileSystemEntries(folderPath)) { if (File.Exists(f)) { //如果有子文件删除文件 File.Delete(f); Console.WriteLine(f); } else { //循环递归删除子文件夹 DeleteDir(f); } } //删除空文件夹 Directory.Delete(folderPath); } } catch (Exception ex) // 异常处理 { Console.WriteLine(ex.Message.ToString()); // 异常信息 } }
3 解压压缩文件
/// <summary> /// 功能:解压zip格式的文件。 /// </summary> /// <param name="zipFilePath">压缩文件路径</param> /// <param name="unZipDir">解压文件存放路径,为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹</param> /// <returns>解压是否成功</returns> public static bool UnZip(string zipFilePath, string unZipDir) { try { if (zipFilePath == string.Empty) { throw new Exception("压缩文件不能为空!"); } if (!File.Exists(zipFilePath)) { throw new FileNotFoundException("压缩文件不存在!"); } //解压文件夹为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹 if (unZipDir == string.Empty) unZipDir = zipFilePath.Replace(Path.GetFileName(zipFilePath), Path.GetFileNameWithoutExtension(zipFilePath)); if (!unZipDir.EndsWith("/")) unZipDir += "/"; if (!Directory.Exists(unZipDir)) Directory.CreateDirectory(unZipDir); using (var s = new ZipInputStream(File.OpenRead(zipFilePath))) { ZipEntry theEntry; while ((theEntry = s.GetNextEntry()) != null) { string directoryName = Path.GetDirectoryName(theEntry.Name); string fileName = Path.GetFileName(theEntry.Name); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(unZipDir + directoryName); } if (directoryName != null && !directoryName.EndsWith("/")) { } if (fileName != String.Empty) { using (FileStream streamWriter = File.Create(unZipDir + theEntry.Name)) { int size; byte[] data = new byte[2048]; while (true) { size = s.Read(data, 0, data.Length); if (size > 0) { streamWriter.Write(data, 0, size); } else { break; } } } } } } return true; } catch (Exception ex) { throw ex; } }
4 单元测试验证
[TestClass] public class ZIPHelperTest { private const string FilePath = @"C:/0f4bb4c4-8508-4487-b586-17a98d372b66"; //读取的文件夹路径 private const string ZipFilePath = @"C:/FileUnitTest.tml"; //压缩后文件存放路径,压缩文件的后缀并不一定是要zip,代码读取不关心后缀 private const string UnZipDir = @"C:/FileUnZipDirUnitTest"; //解压缩后文件存放路径,默认为C盘根目录 /// <summary> /// 压缩所有的文件 /// </summary> [TestMethod] public void CreateZipFileTest() { ZIPHelper.CreateZipFile(FilePath, ZipFilePath); //压缩文件夹 } /// <summary> /// 功能:解压zip格式的文件。 /// </summary> [TestMethod] public void UnZipTest() { var result = ZIPHelper.UnZip(ZipFilePath, UnZipDir); Assert.IsTrue(result); } /// <summary> /// 功能:删除指定路径的文件夹 /// </summary> [TestMethod] public void DeleteDirTest() { ZIPHelper.DeleteDir(FilePath); }
需要特别注意的是,因为临时文件夹是存放在用户本地磁盘上的,所以每次生成的时候都要用一个新的Guid作为文件夹名称,这样才不会重复和覆盖,还有就是压缩文件的后缀并不一定是要zip,代码读取不关心后缀。