FilePackageTool.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. //************************************************************************
  2. // https://github.com/yuzhengyang
  3. // author: yuzhengyang
  4. // date: 2017.6.10 - 2017.6.12
  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. private static string FileTypeDesc = "FilePackage";
  26. private static int FileBuffer = 1024 * 1024;
  27. #region 类型单一,文件处理复杂,加载占用超大内存(这都是辣鸡)
  28. /// <summary>
  29. /// 批量打包任意对象到资源文件
  30. /// </summary>
  31. /// <param name="objCollection">被打包对象的列表。键值对中键为其在资源文件中的唯一标示名。</param>
  32. /// <param name="targetFilePath">目标资源文件。默认参数为当前目录下的"MyRes.pck"文件。</param>
  33. /// <param name="overwrite">是否覆盖已存在的目标文件。默认=True</param>
  34. public static void ResourcePackage(IDictionary<string, object> objCollection, string targetFilePath, bool overwrite = true)
  35. {
  36. if (overwrite) File.Delete(targetFilePath);
  37. using (ResourceWriter rw = new ResourceWriter(targetFilePath))
  38. {
  39. foreach (KeyValuePair<string, object> pair in objCollection)
  40. //为了防传进来的资源名有数字开头,资源名都加了前缀_
  41. rw.AddResource("_" + pair.Key, pair.Value);
  42. rw.Generate();
  43. rw.Close();
  44. }
  45. }
  46. /// <summary>
  47. /// 解包资源文件,返回所有资源及其资源名
  48. /// </summary>
  49. /// <param name="targetFilePath">要解包的资源文件。默认为当前目录下的"MyRes.pck"</param>
  50. /// <returns>资源字典,键值为资源唯一标示名。若无资源返回空集合。</returns>
  51. public static Dictionary<string, object> ResourceUnpack(string targetFilePath)
  52. {
  53. Dictionary<string, object> rtn = new Dictionary<string, object>();
  54. using (ResourceReader rr = new ResourceReader(targetFilePath))
  55. {
  56. foreach (DictionaryEntry entry in rr)
  57. rtn.Add(((string)entry.Key).Substring(1), entry.Value);
  58. }
  59. return rtn;
  60. }
  61. /// <summary>
  62. /// 根据资源名在指定的资源文件中检索资源
  63. /// </summary>
  64. /// <param name="resName">资源名</param>
  65. /// <param name="targetFilePath">要在其中检索的资源文件名,默认为"MyRes.pck"</param>
  66. /// <returns>资源名对应的资源</returns>
  67. public static object ResourceSearch(string resName, string targetFilePath)
  68. {
  69. object rtn = null;
  70. using (ResourceReader rr = new ResourceReader(targetFilePath))
  71. {
  72. foreach (DictionaryEntry entry in rr)
  73. if ((string)entry.Key == '_' + resName)
  74. {
  75. rtn = entry.Value;
  76. break;
  77. }
  78. }
  79. return rtn;
  80. }
  81. /// <summary>
  82. /// 将对象序列化
  83. /// </summary>
  84. /// <param name="FilePath">文件(支持绝大多数数据类型)</param>
  85. /// <param name="obj">要序列化的对象(如哈希表,数组等等)</param>
  86. public static void FileSerialize(string FilePath, object obj)
  87. {
  88. if (File.Exists(FilePath))
  89. {
  90. try
  91. {
  92. FileStream fs = new FileStream(FilePath, FileMode.Create);
  93. BinaryFormatter sl = new BinaryFormatter();
  94. sl.Serialize(fs, obj);
  95. fs.Close();
  96. }
  97. catch
  98. {
  99. //序列化存储失败!
  100. }
  101. }
  102. else
  103. {
  104. //您读取的文件对象不存在
  105. }
  106. }
  107. /// <summary>
  108. /// 将文件反序列化
  109. /// </summary>
  110. /// <param name="FilePath">文件路径(必须是经过当前序列化后的文件)</param>
  111. /// <returns>返回 null 表示序列反解失败或者目标文件不存在</returns>
  112. public static object FileDeSerialize(string FilePath)
  113. {
  114. if (System.IO.File.Exists(FilePath))
  115. {
  116. try
  117. {
  118. FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
  119. BinaryFormatter sl = new BinaryFormatter();
  120. object obg = sl.Deserialize(fs);
  121. fs.Close();
  122. return obg;
  123. }
  124. catch
  125. {
  126. return null;
  127. }
  128. }
  129. else
  130. {
  131. return null;
  132. }
  133. }
  134. #endregion
  135. /// <summary>
  136. /// 打包
  137. /// </summary>
  138. /// <returns>
  139. /// -11;//要打包的路径不存在
  140. /// -12;//打包后的目标文件已存在
  141. /// -13;//要打包的路径中没有文件
  142. /// -404;//未知错误,操作失败
  143. /// </returns>
  144. public static int Pack(string srcPath, string dstFile, bool overwrite = true)
  145. {
  146. DateTime beginTime = DateTime.Now;
  147. if (!Directory.Exists(srcPath)) return -11;//要打包的路径不存在
  148. if (File.Exists(dstFile) && !overwrite) return -12;//打包后的目标文件已存在
  149. List<string> tempfiles = FileTool.GetAllFile(srcPath);
  150. List<FilePackageModel> files = new List<FilePackageModel>();
  151. if (ListTool.HasElements(tempfiles))
  152. {
  153. //汇总所有文件
  154. tempfiles.ForEach(x =>
  155. {
  156. files.Add(new FilePackageModel()
  157. {
  158. Name = Path.GetFileName(x),
  159. Path = DirTool.GetFilePath(x),
  160. Size = FileTool.Size(x),
  161. MD5 = FileTool.GetMD5(x),
  162. });
  163. });
  164. long allfilesize = files.Sum(x => x.Size);
  165. using (FileStream fsWrite = new FileStream(dstFile, FileMode.Create))
  166. {
  167. //写入头部总长度
  168. int headl = files.Sum(x => x.AllByteLength);
  169. byte[] headlength = BitConverter.GetBytes(headl);
  170. fsWrite.Write(headlength, 0, headlength.Length);
  171. //循环写入文件信息
  172. files.ForEach(x =>
  173. {
  174. fsWrite.Write(x.NameLengthByte, 0, x.NameLengthByte.Length);
  175. fsWrite.Write(x.NameByte, 0, x.NameByte.Length);
  176. fsWrite.Write(x.PathLengthByte, 0, x.PathLengthByte.Length);
  177. fsWrite.Write(x.PathByte, 0, x.PathByte.Length);
  178. fsWrite.Write(x.SizeLengthByte, 0, x.SizeLengthByte.Length);
  179. fsWrite.Write(x.SizeByte, 0, x.SizeByte.Length);
  180. fsWrite.Write(x.MD5LengthByte, 0, x.MD5LengthByte.Length);
  181. fsWrite.Write(x.MD5Byte, 0, x.MD5Byte.Length);
  182. });
  183. //循环写入文件
  184. files.ForEach(x =>
  185. {
  186. using (FileStream fsRead = new FileStream(DirTool.Combine(x.Path, x.Name), FileMode.Open))
  187. {
  188. int readCount = 0;
  189. byte[] buffer = new byte[FileBuffer];
  190. while ((readCount = fsRead.Read(buffer, 0, buffer.Length)) > 0)
  191. {
  192. fsWrite.Write(buffer, 0, readCount);
  193. allfilesize -= readCount;
  194. }
  195. fsRead.Close();
  196. }
  197. });
  198. fsWrite.Close();
  199. }
  200. if (allfilesize == 0)
  201. {
  202. return (int)Math.Ceiling((DateTime.Now - beginTime).TotalSeconds);//操作成功
  203. }
  204. }
  205. else
  206. {
  207. return -13;//要打包的路径中没有文件
  208. }
  209. //打包失败后,删除打包后的文件
  210. try { File.Delete(dstFile); } catch (Exception e) { }
  211. return -404;//未知错误,操作失败
  212. }
  213. /// <summary>
  214. /// 解包
  215. /// </summary>
  216. /// <returns></returns>
  217. public static int Unpack(string srcFile, string dstPath, bool overwrite = true)
  218. {
  219. DateTime beginTime = DateTime.Now;
  220. if (!File.Exists(srcFile)) return -11; //要解包的文件不存在
  221. if (Directory.Exists(dstPath) && !overwrite) return -12;//要解包的目标文件夹已存在
  222. using (FileStream fsRead = new FileStream(srcFile, FileMode.Open))
  223. {
  224. //读取头部总长度
  225. byte[] headl = new byte[4];
  226. int headlength = 0;
  227. fsRead.Read(headl, 0, headl.Length);
  228. headlength = BitConverter.ToInt32(headl, 0);
  229. if (headlength > 0)
  230. {
  231. //读取文件列表信息
  232. byte[] headdata = new byte[headlength];
  233. List<FilePackageModel> files = new List<FilePackageModel>();
  234. fsRead.Read(headdata, 0, headlength);
  235. int index = 0;
  236. while (index < headlength)
  237. {
  238. #region 读取文件名长度和内容
  239. //文件名长度
  240. byte[] namelengthbyte = new byte[4];
  241. Buffer.BlockCopy(headdata, index, namelengthbyte, 0, namelengthbyte.Length);
  242. int namelength = BitConverter.ToInt32(namelengthbyte, 0);
  243. index += namelengthbyte.Length;
  244. //文件名内容
  245. byte[] namebyte = new byte[namelength];
  246. Buffer.BlockCopy(headdata, index, namebyte, 0, namelength);
  247. string name = Encoding.Default.GetString(namebyte);
  248. index += namebyte.Length;
  249. #endregion
  250. #region 读取路径长度和内容
  251. //路径长度
  252. byte[] pathlengthbyte = new byte[4];
  253. Buffer.BlockCopy(headdata, index, pathlengthbyte, 0, pathlengthbyte.Length);
  254. int pathlength = BitConverter.ToInt32(pathlengthbyte, 0);
  255. index += pathlengthbyte.Length;
  256. //路径内容
  257. byte[] pathbyte = new byte[pathlength];
  258. Buffer.BlockCopy(headdata, index, pathbyte, 0, pathlength);
  259. string path = Encoding.Default.GetString(pathbyte);
  260. index += pathbyte.Length;
  261. #endregion
  262. #region 读取文件大小长度和内容
  263. //文件大小长度
  264. byte[] sizelengthbyte = new byte[4];
  265. Buffer.BlockCopy(headdata, index, sizelengthbyte, 0, sizelengthbyte.Length);
  266. int sizelength = BitConverter.ToInt32(sizelengthbyte, 0);
  267. index += sizelengthbyte.Length;
  268. //文件大小
  269. byte[] sizebyte = new byte[sizelength];
  270. Buffer.BlockCopy(headdata, index, sizebyte, 0, sizelength);
  271. int size = BitConverter.ToInt32(sizebyte, 4);
  272. index += sizebyte.Length;
  273. #endregion
  274. #region 读取文件MD5码长度和内容
  275. //文件大小长度
  276. byte[] md5lengthbyte = new byte[4];
  277. Buffer.BlockCopy(headdata, index, md5lengthbyte, 0, md5lengthbyte.Length);
  278. int md5length = BitConverter.ToInt32(md5lengthbyte, 0);
  279. index += md5lengthbyte.Length;
  280. //文件大小
  281. byte[] md5byte = new byte[md5length];
  282. Buffer.BlockCopy(headdata, index, md5byte, 0, md5length);
  283. string md5 = Encoding.Default.GetString(md5byte);
  284. index += md5byte.Length;
  285. #endregion
  286. files.Add(new FilePackageModel()
  287. {
  288. Name = name,
  289. Path = path,
  290. Size = size,
  291. MD5 = md5,
  292. });
  293. }
  294. int a = 111;
  295. //fsWrite.Write(x.SizeLengthByte, 0, x.SizeLengthByte.Length);
  296. //fsWrite.Write(x.SizeByte, 0, x.SizeByte.Length);
  297. //fsWrite.Write(x.MD5LengthByte, 0, x.MD5LengthByte.Length);
  298. //fsWrite.Write(x.MD5Byte, 0, x.MD5Byte.Length);
  299. }
  300. }
  301. return (int)Math.Ceiling((DateTime.Now - beginTime).TotalSeconds);//操作成功
  302. }
  303. }
  304. }