最近在做的一個需求是這樣的:將導出的所有文件壓縮爲一個壓縮文件,導入的時候再解壓該壓縮文件並讀取文件內容。要解決這個問題,需要做如下幾步操作方能實現:
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,代碼讀取不關心後綴。