//************************************************************************ // https://github.com/yuzhengyang // author: yuzhengyang // date: 2017.6.10 - 2017.6.12 // desc: 文件打包工具 // Copyright (c) yuzhengyang. All rights reserved. //************************************************************************ 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; using Y.Utils.DataUtils.Collections; using Y.Utils.IOUtils.PathUtils; namespace Y.Utils.IOUtils.FileUtils { /// /// 文件打包工具 /// public class FilePackageTool { private static string FileTypeDesc = "FilePackage"; private static int FileBuffer = 1024 * 1024; #region 类型单一,文件处理复杂,加载占用超大内存(这都是辣鸡) /// /// 批量打包任意对象到资源文件 /// /// 被打包对象的列表。键值对中键为其在资源文件中的唯一标示名。 /// 目标资源文件。默认参数为当前目录下的"MyRes.pck"文件。 /// 是否覆盖已存在的目标文件。默认=True public static void ResourcePackage(IDictionary objCollection, string targetFilePath, bool overwrite = true) { if (overwrite) File.Delete(targetFilePath); using (ResourceWriter rw = new ResourceWriter(targetFilePath)) { foreach (KeyValuePair pair in objCollection) //为了防传进来的资源名有数字开头,资源名都加了前缀_ rw.AddResource("_" + pair.Key, pair.Value); rw.Generate(); rw.Close(); } } /// /// 解包资源文件,返回所有资源及其资源名 /// /// 要解包的资源文件。默认为当前目录下的"MyRes.pck" /// 资源字典,键值为资源唯一标示名。若无资源返回空集合。 public static Dictionary ResourceUnpack(string targetFilePath) { Dictionary rtn = new Dictionary(); using (ResourceReader rr = new ResourceReader(targetFilePath)) { foreach (DictionaryEntry entry in rr) rtn.Add(((string)entry.Key).Substring(1), entry.Value); } return rtn; } /// /// 根据资源名在指定的资源文件中检索资源 /// /// 资源名 /// 要在其中检索的资源文件名,默认为"MyRes.pck" /// 资源名对应的资源 public static object ResourceSearch(string resName, string targetFilePath) { object rtn = null; using (ResourceReader rr = new ResourceReader(targetFilePath)) { foreach (DictionaryEntry entry in rr) if ((string)entry.Key == '_' + resName) { rtn = entry.Value; break; } } return rtn; } /// /// 将对象序列化 /// /// 文件(支持绝大多数数据类型) /// 要序列化的对象(如哈希表,数组等等) public static void FileSerialize(string FilePath, object obj) { if (File.Exists(FilePath)) { try { FileStream fs = new FileStream(FilePath, FileMode.Create); BinaryFormatter sl = new BinaryFormatter(); sl.Serialize(fs, obj); fs.Close(); } catch { //序列化存储失败! } } else { //您读取的文件对象不存在 } } /// /// 将文件反序列化 /// /// 文件路径(必须是经过当前序列化后的文件) /// 返回 null 表示序列反解失败或者目标文件不存在 public static object FileDeSerialize(string FilePath) { if (System.IO.File.Exists(FilePath)) { try { FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read); BinaryFormatter sl = new BinaryFormatter(); object obg = sl.Deserialize(fs); fs.Close(); return obg; } catch { return null; } } else { return null; } } #endregion /// /// 打包 /// /// /// -11;//要打包的路径不存在 /// -12;//打包后的目标文件已存在 /// -13;//要打包的路径中没有文件 /// -404;//未知错误,操作失败 /// public static int Pack(string srcPath, string dstFile, bool overwrite = true) { DateTime beginTime = DateTime.Now; if (!Directory.Exists(srcPath)) return -11;//要打包的路径不存在 if (File.Exists(dstFile) && !overwrite) return -12;//打包后的目标文件已存在 List tempfiles = FileTool.GetAllFile(srcPath); List files = new List(); if (ListTool.HasElements(tempfiles)) { //汇总所有文件 tempfiles.ForEach(x => { files.Add(new FilePackageModel() { Name = Path.GetFileName(x), Path = DirTool.GetFilePath(x), Size = FileTool.Size(x), MD5 = FileTool.GetMD5(x), }); }); long allfilesize = files.Sum(x => x.Size); using (FileStream fsWrite = new FileStream(dstFile, FileMode.Create)) { //写入头部总长度 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 => { using (FileStream fsRead = new FileStream(DirTool.Combine(x.Path, x.Name), FileMode.Open)) { int readCount = 0; byte[] buffer = new byte[FileBuffer]; while ((readCount = fsRead.Read(buffer, 0, buffer.Length)) > 0) { fsWrite.Write(buffer, 0, readCount); allfilesize -= readCount; } fsRead.Close(); } }); fsWrite.Close(); } if (allfilesize == 0) { return (int)Math.Ceiling((DateTime.Now - beginTime).TotalSeconds);//操作成功 } } else { return -13;//要打包的路径中没有文件 } //打包失败后,删除打包后的文件 try { File.Delete(dstFile); } catch (Exception e) { } return -404;//未知错误,操作失败 } /// /// 解包 /// /// public static int Unpack(string srcFile, string dstPath, 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)) { //读取头部总长度 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]; List files = new List(); fsRead.Read(headdata, 0, headlength); int index = 0; while (index < headlength) { #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); int size = BitConverter.ToInt32(sizebyte, 4); 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, }); } int a = 111; //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); } } return (int)Math.Ceiling((DateTime.Now - beginTime).TotalSeconds);//操作成功 } } }