最近在做的一个需求是这样的:将导出的所有文件压缩为一个压缩文件,导入的时候再解压该压缩文件并读取文件内容。要解决这个问题,需要做如下几步操作方能实现:
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,代码读取不关心后缀。