//************************************************************************
// author: yuzhengyang
// date: 2017.6.10 - 2017.6.15
// desc: 文件打包工具
// Copyright (c) yuzhengyang. All rights reserved.
//************************************************************************
using Azylee.Core.DataUtils.CollectionUtils;
using Azylee.Core.DelegateUtils.ProcessDelegateUtils;
using Azylee.Core.IOUtils.DirUtils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Resources;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
namespace Azylee.Core.IOUtils.FileUtils
{
///
/// 文件打包工具
///
public class FilePackageTool
{
const string FileType = "Y.Utils.FilePackage";//文件类型 禁止修改长度(19位)
const string FileVersion = "100001";//类型的版本 禁止修改长度(6位)
private static int FileBuffer = 1024 * 1024;
///
/// 文件打包
///
/// 要打包的路径
/// 打包后的文件
/// 回调进度
/// 覆盖打包后的文件(重复时)
///
/// -11;//要打包的路径不存在
/// -12;//打包后的目标文件已存在
/// -13;//要打包的路径中没有文件
/// -14;//输出文件夹不存在
/// -404;//未知错误,操作失败
///
public static int Pack(string srcPath, string dstFile, ProgressDelegate.ProgressHandler progress = null, object sender = null, bool overwrite = true)
{
DateTime beginTime = DateTime.Now;
if (!Directory.Exists(srcPath)) return -11;//要打包的路径不存在
if (File.Exists(dstFile) && !overwrite) return -12;//打包后的目标文件已存在
if (!DirTool.Create(DirTool.GetFilePath(dstFile))) return -14;//输出文件夹不存在
List tempfiles = FileTool.GetAllFile(srcPath);
List files = CreateFilePackageModel(tempfiles, srcPath);
if (ListTool.HasElements(files))
{
long allfilesize = files.Sum(x => x.Size);//文件总大小
long surplusfilesize = allfilesize;//剩余要写入的文件大小
using (FileStream fsWrite = new FileStream(dstFile, FileMode.Create))
{
try
{
//写入文件类型标识和版本号
byte[] filetypeandversion = Encoding.Default.GetBytes(FileType + FileVersion);
fsWrite.Write(filetypeandversion, 0, filetypeandversion.Length);
//写入头部总长度
int headl = files.Sum(x => x.AllByteLength);
byte[] headlength = BitConverter.GetBytes(headl);
fsWrite.Write(headlength, 0, headlength.Length);
//循环写入文件信息
files.ForEach(x =>
{
fsWrite.Write(x.NameLengthByte, 0, x.NameLengthByte.Length);
fsWrite.Write(x.NameByte, 0, x.NameByte.Length);
fsWrite.Write(x.PathLengthByte, 0, x.PathLengthByte.Length);
fsWrite.Write(x.PathByte, 0, x.PathByte.Length);
fsWrite.Write(x.SizeLengthByte, 0, x.SizeLengthByte.Length);
fsWrite.Write(x.SizeByte, 0, x.SizeByte.Length);
fsWrite.Write(x.MD5LengthByte, 0, x.MD5LengthByte.Length);
fsWrite.Write(x.MD5Byte, 0, x.MD5Byte.Length);
});
//循环写入文件
files.ForEach(x =>
{
//读取文件(可访问被打开的exe文件)
using (FileStream fsRead = new FileStream(DirTool.Combine(srcPath, x.Path, x.Name), FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
int readCount = 0;
byte[] buffer = new byte[FileBuffer];
while ((readCount = fsRead.Read(buffer, 0, buffer.Length)) > 0)
{
fsWrite.Write(buffer, 0, readCount);
surplusfilesize -= readCount;
progress?.Invoke(sender, new ProgressEventArgs(allfilesize - surplusfilesize, allfilesize));
}
}
});
}
catch (Exception e) { }
}
if (surplusfilesize == 0)
{
return (int)Math.Ceiling((DateTime.Now - beginTime).TotalSeconds);//操作成功
}
}
else
{
return -13;//要打包的路径中没有文件
}
//打包失败后,删除打包后的文件
try { File.Delete(dstFile); } catch (Exception e) { }
return -404;//未知错误,操作失败
}
///
/// 拆包
///
/// 包文件路径
/// 拆包到的目录
/// 回调进度
/// 覆盖拆包后的文件(重复时)
///
/// -11; //要解包的文件不存在
/// -12;//要解包的目标文件夹已存在
/// -20;// 文件类型不匹配
/// -99;//未知错误,操作失败
///
public static int Unpack(string srcFile, string dstPath, ProgressDelegate.ProgressHandler progress = null, object sender = null, bool overwrite = true)
{
DateTime beginTime = DateTime.Now;
if (!File.Exists(srcFile)) return -11; //要解包的文件不存在
if (Directory.Exists(dstPath) && !overwrite) return -12;//要解包的目标文件夹已存在
using (FileStream fsRead = new FileStream(srcFile, FileMode.Open))
{
try
{
string version = GetFileVersion(fsRead);
if (version == null) return -20;// 文件类型不匹配
//读取头部总长度
byte[] headl = new byte[4];
int headlength = 0;
fsRead.Read(headl, 0, headl.Length);
headlength = BitConverter.ToInt32(headl, 0);
if (headlength > 0)
{
//读取文件列表信息
byte[] headdata = new byte[headlength];
fsRead.Read(headdata, 0, headlength);
List files = GetFilePackageModel(headdata);
if (ListTool.HasElements(files))
{
long allfilesize = files.Sum(x => x.Size);//文件总大小
long current = 0;//当前进度
//读取写出所有文件
files.ForEach(x =>
{
if (DirTool.Create(DirTool.Combine(dstPath, x.Path)))
{
try
{
using (FileStream fsWrite = new FileStream(DirTool.Combine(dstPath, x.Path, x.Name), FileMode.Create))
{
long size = x.Size;
int readCount = 0;
byte[] buffer = new byte[FileBuffer];
while (size > FileBuffer)
{
readCount = fsRead.Read(buffer, 0, buffer.Length);
fsWrite.Write(buffer, 0, readCount);
size -= readCount;
current += readCount;
progress?.Invoke(sender, new ProgressEventArgs(current, allfilesize));
}
if (size <= FileBuffer)
{
readCount = fsRead.Read(buffer, 0, (int)size);
fsWrite.Write(buffer, 0, readCount);
current += readCount;
progress?.Invoke(sender, new ProgressEventArgs(current, allfilesize));
}
}
}
catch (Exception e)
{
fsRead.Seek(x.Size, SeekOrigin.Current);
current += x.Size;
progress?.Invoke(sender, new ProgressEventArgs(current, allfilesize));
}
}
});
//验证文件列表
bool allCheck = true;
foreach (var file in files)
{
string temp = DirTool.Combine(dstPath, file.Path, file.Name);
string tempCk = FileTool.GetMD5(temp);
if (tempCk != file.MD5)//验证文件(Size:速度会快一些,MD5在大文件的验证上非常耗时)
{
allCheck = false;
break;
}
}
if (allCheck) return (int)Math.Ceiling((DateTime.Now - beginTime).TotalSeconds);//操作成功
}
}
}
catch (Exception e) { }
}
return -99;//未知错误,操作失败
}
///
/// 获取文件类型的类型版本
///
///
///
/// 如果文件类型不匹配,则返回null
///
private static string GetFileVersion(FileStream fs)
{
string result = null;
try
{
//读取文件类型标识和版本号
byte[] filetype = Encoding.Default.GetBytes(FileType);
fs.Read(filetype, 0, filetype.Length);
string filetypestr = Encoding.Default.GetString(filetype);
byte[] fileversion = Encoding.Default.GetBytes(FileVersion);
fs.Read(fileversion, 0, fileversion.Length);
string fileversionstr = Encoding.Default.GetString(fileversion);
//如果文件类型匹配,则返回版本号
if (filetypestr == FileType) result = fileversionstr;
}
catch (Exception e) { }
return result;
}
///
/// 解析打包文件文件列表
///
///
///
private static List GetFilePackageModel(byte[] headdata)
{
List files = new List();
int index = 0;
try
{
while (index < headdata.Length)
{
#region 读取文件名长度和内容
//文件名长度
byte[] namelengthbyte = new byte[4];
Buffer.BlockCopy(headdata, index, namelengthbyte, 0, namelengthbyte.Length);
int namelength = BitConverter.ToInt32(namelengthbyte, 0);
index += namelengthbyte.Length;
//文件名内容
byte[] namebyte = new byte[namelength];
Buffer.BlockCopy(headdata, index, namebyte, 0, namelength);
string name = Encoding.Default.GetString(namebyte);
index += namebyte.Length;
#endregion
#region 读取路径长度和内容
//路径长度
byte[] pathlengthbyte = new byte[4];
Buffer.BlockCopy(headdata, index, pathlengthbyte, 0, pathlengthbyte.Length);
int pathlength = BitConverter.ToInt32(pathlengthbyte, 0);
index += pathlengthbyte.Length;
//路径内容
byte[] pathbyte = new byte[pathlength];
Buffer.BlockCopy(headdata, index, pathbyte, 0, pathlength);
string path = Encoding.Default.GetString(pathbyte);
index += pathbyte.Length;
#endregion
#region 读取文件大小长度和内容
//文件大小长度
byte[] sizelengthbyte = new byte[4];
Buffer.BlockCopy(headdata, index, sizelengthbyte, 0, sizelengthbyte.Length);
int sizelength = BitConverter.ToInt32(sizelengthbyte, 0);
index += sizelengthbyte.Length;
//文件大小
byte[] sizebyte = new byte[sizelength];
Buffer.BlockCopy(headdata, index, sizebyte, 0, sizelength);
long size = BitConverter.ToInt64(sizebyte, 0);
index += sizebyte.Length;
#endregion
#region 读取文件MD5码长度和内容
//文件大小长度
byte[] md5lengthbyte = new byte[4];
Buffer.BlockCopy(headdata, index, md5lengthbyte, 0, md5lengthbyte.Length);
int md5length = BitConverter.ToInt32(md5lengthbyte, 0);
index += md5lengthbyte.Length;
//文件大小
byte[] md5byte = new byte[md5length];
Buffer.BlockCopy(headdata, index, md5byte, 0, md5length);
string md5 = Encoding.Default.GetString(md5byte);
index += md5byte.Length;
#endregion
files.Add(new FilePackageModel()
{
Name = name,
Path = path,
Size = size,
MD5 = md5,
});
}
return files;
}
catch (Exception e) { return null; }
}
///
/// 创建打包文件列表信息
///
///
///
///
private static List CreateFilePackageModel(List files, string srcPath)
{
if (ListTool.IsNullOrEmpty(files)) return null;
List result = new List();
//汇总所有文件
files.ForEach(x =>
{
result.Add(new FilePackageModel()
{
Name = Path.GetFileName(x),
Path = DirTool.GetFilePath(x).Substring(srcPath.Count()),
Size = FileTool.Size(x),
MD5 = FileTool.GetMD5(x),
});
});
return result;
}
}
}