FilePackageTool.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. //************************************************************************
  2. // https://github.com/yuzhengyang
  3. // author: yuzhengyang
  4. // date: 2017.6.10 - 2017.6.15
  5. // desc: 文件打包工具
  6. // Copyright (c) yuzhengyang. All rights reserved.
  7. //************************************************************************
  8. using System;
  9. using System.Collections;
  10. using System.Collections.Generic;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Resources;
  14. using System.Runtime.Serialization.Formatters.Binary;
  15. using System.Text;
  16. using Y.Utils.DataUtils.Collections;
  17. using Y.Utils.IOUtils.PathUtils;
  18. namespace Y.Utils.IOUtils.FileUtils
  19. {
  20. /// <summary>
  21. /// 文件打包工具
  22. /// </summary>
  23. public class FilePackageTool
  24. {
  25. const string FileType = "Y.Utils.FilePackage";//文件类型 禁止修改长度(19位)
  26. const string FileVersion = "100001";//类型的版本 禁止修改长度(6位)
  27. private static int FileBuffer = 1024 * 1024;
  28. /// <summary>
  29. /// 打包
  30. /// </summary>
  31. /// <returns>
  32. /// -11;//要打包的路径不存在
  33. /// -12;//打包后的目标文件已存在
  34. /// -13;//要打包的路径中没有文件
  35. /// -404;//未知错误,操作失败
  36. /// </returns>
  37. public static int Pack(string srcPath, string dstFile, bool overwrite = true)
  38. {
  39. DateTime beginTime = DateTime.Now;
  40. if (!Directory.Exists(srcPath)) return -11;//要打包的路径不存在
  41. if (File.Exists(dstFile) && !overwrite) return -12;//打包后的目标文件已存在
  42. List<string> tempfiles = FileTool.GetAllFile(srcPath);
  43. List<FilePackageModel> files = CreateFilePackageModel(tempfiles, srcPath);
  44. if (ListTool.HasElements(files))
  45. {
  46. long allfilesize = files.Sum(x => x.Size);
  47. using (FileStream fsWrite = new FileStream(dstFile, FileMode.Create))
  48. {
  49. try
  50. {
  51. //写入文件类型标识和版本号
  52. byte[] filetypeandversion = Encoding.Default.GetBytes(FileType + FileVersion);
  53. fsWrite.Write(filetypeandversion, 0, filetypeandversion.Length);
  54. //写入头部总长度
  55. int headl = files.Sum(x => x.AllByteLength);
  56. byte[] headlength = BitConverter.GetBytes(headl);
  57. fsWrite.Write(headlength, 0, headlength.Length);
  58. //循环写入文件信息
  59. files.ForEach(x =>
  60. {
  61. fsWrite.Write(x.NameLengthByte, 0, x.NameLengthByte.Length);
  62. fsWrite.Write(x.NameByte, 0, x.NameByte.Length);
  63. fsWrite.Write(x.PathLengthByte, 0, x.PathLengthByte.Length);
  64. fsWrite.Write(x.PathByte, 0, x.PathByte.Length);
  65. fsWrite.Write(x.SizeLengthByte, 0, x.SizeLengthByte.Length);
  66. fsWrite.Write(x.SizeByte, 0, x.SizeByte.Length);
  67. fsWrite.Write(x.MD5LengthByte, 0, x.MD5LengthByte.Length);
  68. fsWrite.Write(x.MD5Byte, 0, x.MD5Byte.Length);
  69. });
  70. //循环写入文件
  71. files.ForEach(x =>
  72. {
  73. using (FileStream fsRead = new FileStream(DirTool.Combine(srcPath, x.Path, x.Name), FileMode.Open))
  74. {
  75. int readCount = 0;
  76. byte[] buffer = new byte[FileBuffer];
  77. while ((readCount = fsRead.Read(buffer, 0, buffer.Length)) > 0)
  78. {
  79. fsWrite.Write(buffer, 0, readCount);
  80. allfilesize -= readCount;
  81. }
  82. }
  83. });
  84. }
  85. catch (Exception e) { }
  86. }
  87. if (allfilesize == 0)
  88. {
  89. return (int)Math.Ceiling((DateTime.Now - beginTime).TotalSeconds);//操作成功
  90. }
  91. }
  92. else
  93. {
  94. return -13;//要打包的路径中没有文件
  95. }
  96. //打包失败后,删除打包后的文件
  97. try { File.Delete(dstFile); } catch (Exception e) { }
  98. return -404;//未知错误,操作失败
  99. }
  100. /// <summary>
  101. /// 解包
  102. /// </summary>
  103. /// <returns>
  104. /// -11; //要解包的文件不存在
  105. /// -12;//要解包的目标文件夹已存在
  106. /// -20;// 文件类型不匹配
  107. /// -404;//未知错误,操作失败
  108. /// </returns>
  109. public static int Unpack(string srcFile, string dstPath, bool overwrite = true)
  110. {
  111. DateTime beginTime = DateTime.Now;
  112. if (!File.Exists(srcFile)) return -11; //要解包的文件不存在
  113. if (Directory.Exists(dstPath) && !overwrite) return -12;//要解包的目标文件夹已存在
  114. using (FileStream fsRead = new FileStream(srcFile, FileMode.Open))
  115. {
  116. try
  117. {
  118. string version = GetFileVersion(fsRead);
  119. if (version == null) return -20;// 文件类型不匹配
  120. //读取头部总长度
  121. byte[] headl = new byte[4];
  122. int headlength = 0;
  123. fsRead.Read(headl, 0, headl.Length);
  124. headlength = BitConverter.ToInt32(headl, 0);
  125. if (headlength > 0)
  126. {
  127. //读取文件列表信息
  128. byte[] headdata = new byte[headlength];
  129. fsRead.Read(headdata, 0, headlength);
  130. List<FilePackageModel> files = GetFilePackageModel(headdata);
  131. if (ListTool.HasElements(files))
  132. {
  133. //读取写出所有文件
  134. files.ForEach(x =>
  135. {
  136. if (DirTool.Create(DirTool.Combine(dstPath, x.Path)))
  137. {
  138. using (FileStream fsWrite = new FileStream(DirTool.Combine(dstPath, x.Path, x.Name), FileMode.Create))
  139. {
  140. long size = x.Size;
  141. int readCount = 0;
  142. byte[] buffer = new byte[FileBuffer];
  143. while (size > FileBuffer)
  144. {
  145. readCount = fsRead.Read(buffer, 0, buffer.Length);
  146. fsWrite.Write(buffer, 0, readCount);
  147. size -= readCount;
  148. }
  149. if (size <= FileBuffer)
  150. {
  151. readCount = fsRead.Read(buffer, 0, (int)size);
  152. fsWrite.Write(buffer, 0, readCount);
  153. }
  154. }
  155. }
  156. });
  157. //验证文件列表
  158. bool allCheck = true;
  159. foreach (var file in files)
  160. {
  161. string temp = DirTool.Combine(dstPath, file.Path, file.Name);
  162. string tempMD5 = FileTool.GetMD5(temp);
  163. if (tempMD5 != file.MD5)//验证文件MD5不匹配则跳出验证
  164. {
  165. allCheck = false;
  166. break;
  167. }
  168. }
  169. if (allCheck) return (int)Math.Ceiling((DateTime.Now - beginTime).TotalSeconds);//操作成功
  170. }
  171. }
  172. }
  173. catch (Exception e) { }
  174. }
  175. return -404;//未知错误,操作失败
  176. }
  177. /// <summary>
  178. /// 获取文件类型的类型版本
  179. /// </summary>
  180. /// <param name="fs"></param>
  181. /// <returns>
  182. /// 如果文件类型不匹配,则返回null
  183. /// </returns>
  184. private static string GetFileVersion(FileStream fs)
  185. {
  186. string result = null;
  187. try
  188. {
  189. //读取文件类型标识和版本号
  190. byte[] filetype = Encoding.Default.GetBytes(FileType);
  191. fs.Read(filetype, 0, filetype.Length);
  192. string filetypestr = Encoding.Default.GetString(filetype);
  193. byte[] fileversion = Encoding.Default.GetBytes(FileVersion);
  194. fs.Read(fileversion, 0, fileversion.Length);
  195. string fileversionstr = Encoding.Default.GetString(fileversion);
  196. //如果文件类型匹配,则返回版本号
  197. if (filetypestr == FileType) result = fileversionstr;
  198. }
  199. catch (Exception e) { }
  200. return result;
  201. }
  202. /// <summary>
  203. /// 解析打包文件文件列表
  204. /// </summary>
  205. /// <param name="headdata"></param>
  206. /// <returns></returns>
  207. private static List<FilePackageModel> GetFilePackageModel(byte[] headdata)
  208. {
  209. List<FilePackageModel> files = new List<FilePackageModel>();
  210. int index = 0;
  211. try
  212. {
  213. while (index < headdata.Length)
  214. {
  215. #region 读取文件名长度和内容
  216. //文件名长度
  217. byte[] namelengthbyte = new byte[4];
  218. Buffer.BlockCopy(headdata, index, namelengthbyte, 0, namelengthbyte.Length);
  219. int namelength = BitConverter.ToInt32(namelengthbyte, 0);
  220. index += namelengthbyte.Length;
  221. //文件名内容
  222. byte[] namebyte = new byte[namelength];
  223. Buffer.BlockCopy(headdata, index, namebyte, 0, namelength);
  224. string name = Encoding.Default.GetString(namebyte);
  225. index += namebyte.Length;
  226. #endregion
  227. #region 读取路径长度和内容
  228. //路径长度
  229. byte[] pathlengthbyte = new byte[4];
  230. Buffer.BlockCopy(headdata, index, pathlengthbyte, 0, pathlengthbyte.Length);
  231. int pathlength = BitConverter.ToInt32(pathlengthbyte, 0);
  232. index += pathlengthbyte.Length;
  233. //路径内容
  234. byte[] pathbyte = new byte[pathlength];
  235. Buffer.BlockCopy(headdata, index, pathbyte, 0, pathlength);
  236. string path = Encoding.Default.GetString(pathbyte);
  237. index += pathbyte.Length;
  238. #endregion
  239. #region 读取文件大小长度和内容
  240. //文件大小长度
  241. byte[] sizelengthbyte = new byte[4];
  242. Buffer.BlockCopy(headdata, index, sizelengthbyte, 0, sizelengthbyte.Length);
  243. int sizelength = BitConverter.ToInt32(sizelengthbyte, 0);
  244. index += sizelengthbyte.Length;
  245. //文件大小
  246. byte[] sizebyte = new byte[sizelength];
  247. Buffer.BlockCopy(headdata, index, sizebyte, 0, sizelength);
  248. long size = BitConverter.ToInt64(sizebyte, 0);
  249. index += sizebyte.Length;
  250. #endregion
  251. #region 读取文件MD5码长度和内容
  252. //文件大小长度
  253. byte[] md5lengthbyte = new byte[4];
  254. Buffer.BlockCopy(headdata, index, md5lengthbyte, 0, md5lengthbyte.Length);
  255. int md5length = BitConverter.ToInt32(md5lengthbyte, 0);
  256. index += md5lengthbyte.Length;
  257. //文件大小
  258. byte[] md5byte = new byte[md5length];
  259. Buffer.BlockCopy(headdata, index, md5byte, 0, md5length);
  260. string md5 = Encoding.Default.GetString(md5byte);
  261. index += md5byte.Length;
  262. #endregion
  263. files.Add(new FilePackageModel()
  264. {
  265. Name = name,
  266. Path = path,
  267. Size = size,
  268. MD5 = md5,
  269. });
  270. }
  271. return files;
  272. }
  273. catch (Exception e) { return null; }
  274. }
  275. /// <summary>
  276. /// 创建打包文件列表信息
  277. /// </summary>
  278. /// <param name="files"></param>
  279. /// <param name="srcPath"></param>
  280. /// <returns></returns>
  281. private static List<FilePackageModel> CreateFilePackageModel(List<string> files, string srcPath)
  282. {
  283. if (ListTool.IsNullOrEmpty(files)) return null;
  284. List<FilePackageModel> result = new List<FilePackageModel>();
  285. //汇总所有文件
  286. files.ForEach(x =>
  287. {
  288. result.Add(new FilePackageModel()
  289. {
  290. Name = Path.GetFileName(x),
  291. Path = DirTool.GetFilePath(x).Substring(srcPath.Count()),
  292. Size = FileTool.Size(x),
  293. MD5 = FileTool.GetMD5(x),
  294. });
  295. });
  296. return result;
  297. }
  298. }
  299. }