|
背景:最近闲来无事,也应论坛某会员要求,想做个QQ表情下载的站点。本来事情是很简单的,写个小小的CRUD也就可以了,但嘻哈呵嘿既然是个.Net程序员,当然要使用.Net来实现了。今天我们就用.Net来实现CFC ( custom face cab? ) 的表情格式的打包功能。 要做到这个功能,我们必须先了解这个格式,首先Google一下。我们找到了这一篇来自清华大学的文章:FC文件格式详解 从这篇文章里我们得知了CFC的文件格式大概如下: 一个块有15个字段,如下 md5的字符串形式长度,4个字节 快捷键长度,4字节 表情名称长度,4字节 表情文件名长度,4字节 表情文件长度,4字节 微缩图文件名长度,4字节 微缩文件长度,4字节 表情文件帧数,4字节 图片md5的字符串形式 快捷键 表情名称 表情文件名 微缩图文件名 表情文件内容 微缩图内容 知道了格式就好办了,我们按步就班定义一个结构(struct) 1 Struct#region Struct 2 public struct FaceBlock 3 { 4 public uint MD5Length; //32 5 public uint uintcutLength; //4 6 public uint FaceNameLength; //4 7 public uint FaceFileNameLength; //36 md5 + extension 8 public uint FileLength; 9 public uint ThumbnailFileNameLength; //41 md5 + fixed.bmp 10 public uint ThumbnailFileLength; 11 public uint FrameLength; 12 public string MD5; 13 public string uintcuts; 14 public string FaceName; 15 public string FaceFileName; 16 public string ThumbnailFileName; 17 public byte[] FaceData; 18 public byte[] ThumbnailData; 19 20 public static FaceBlock FromImage(string file) 21 { 22 return FaceHelper.GetFaceBlockFromImage(file); 23 } 24 25 byte[] GetBytes(uint value) 26 { 27 byte[] bt = BitConverter.GetBytes(value); 28 List<byte> bytes = new List<byte>(); 29 bytes.AddRange(bt); 30 if (bytes.Count < 4) 31 { 32 int l = 4 - bytes.Count; 33 for (int i = 0; i < l; i++) 34 bytes.Add((byte)0); 35 } 36 return bytes.ToArray(); 37 } 38 39 public byte[] ToBytes() 40 { 41 List<byte> bytes = new List<byte>(); 42 Encoding e = Encoding.ASCII; 43 bytes.AddRange(GetBytes(MD5Length)); 44 bytes.AddRange(GetBytes(uintcutLength)); 45 bytes.AddRange(GetBytes(FaceNameLength)); 46 bytes.AddRange(GetBytes(FaceFileNameLength)); 47 bytes.AddRange(GetBytes(FileLength)); 48 bytes.AddRange(GetBytes(ThumbnailFileNameLength)); 49 bytes.AddRange(GetBytes(ThumbnailFileLength)); 50 bytes.AddRange(GetBytes(FrameLength)); 51 52 bytes.AddRange(e.GetBytes(MD5)); 53 bytes.AddRange(e.GetBytes(uintcuts)); 54 bytes.AddRange(e.GetBytes(FaceName)); 55 bytes.AddRange(e.GetBytes(FaceFileName)); 56 bytes.AddRange(e.GetBytes(ThumbnailFileName)); 57 58 bytes.AddRange(FaceData); 59 bytes.AddRange(ThumbnailData); 60 61 return bytes.ToArray(); 62 } 63 } 64 #endregion其中含有两方法,一个是从文件得到一个此结构的静态方法,另一个是将此结构转化为byte数组。 我们再建一个类,命名为:FaceHelper 代码如下: public class FaceHelper { internal static FaceBlock GetFaceBlockFromImage(string file) { FaceBlock fb = new FaceBlock(); //打开文件流 FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read); //获取图像 Image img = Image.FromStream(fs); //获得一个20*20的缩略图 Image thumbnail = img.GetThumbnailImage(20, 20, null, IntPtr.Zero); MemoryStream ms = new MemoryStream(); //将缩图图转化数byte数组 thumbnail.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); byte[] thumbnailData = ms.ToArray(); ms.Close(); ms.Dispose(); thumbnail.Dispose(); //得到一个唯一的MD5字符串 string md5 = GetMD5(fs); //文件名,格式为:md5 + 扩展名 string fileName = string.Format("{0}{1}", md5, Path.GetExtension(file)); //缩略图文件名,格式为:md5 + fixed.bmp string thumbnailName = string.Format("{0}fixed.bmp", md5); //随机设置一个快捷键 string uintcuts = "qq.5inet.net_" + RandomNum(6); fs.Close(); fs.Dispose(); //取得总的帧数 System.Drawing.Imaging.FrameDimension fd = System.Drawing.Imaging.FrameDimension.Resolution; int frameCount = img.FrameDimensionsList.Length; img.Dispose(); fb.MD5 = md5; fb.MD5Length = (uint)md5.Length; fb.uintcuts = uintcuts; fb.uintcutLength = (uint)uintcuts.Length; fb.FaceName = uintcuts; fb.FaceNameLength = (uint)uintcuts.Length; fb.FaceFileName = fileName; fb.FaceFileNameLength = (uint)fileName.Length; fb.ThumbnailFileName = thumbnailName; fb.ThumbnailFileNameLength = (uint)thumbnailName.Length; fb.FaceData = File.ReadAllBytes(file); fb.FileLength = (uint)fb.FaceData.Length; fb.ThumbnailData = thumbnailData; fb.ThumbnailFileLength = (uint)thumbnailData.Length; fb.FrameLength = (uint)frameCount; return fb; } Helper#region Helper //随机方法 internal static string RandomNum(int n) // { string strchar = "0,1,2,3,4,5,6,7,8,9"; string[] VcArray = strchar.Split(','); string VNum = "";//由于字符串很短,F77pclw,c络G|?,业,e'b就不用StringBuilder了 int temp = -1; //记录上次随机数值,尽量避免产生几个一样的随机数 //采用一个简单的算法以保证生成随机数的不同 Random rand = new Random(); for (int i = 1; i < n + 1; i++) { if (temp != -1) { rand = new Random(i * temp * unchecked((int) DateTime.Now.Ticks)); } //int t = rand.Next(35) ; int t = rand.Next(10); if (temp != -1 && temp == t) { return RandomNum(n); } temp = t; VNum += VcArray[t]; } return VNum;//返回生成的随机数 } //从文件名获得MD5 internal static string GetMD5(FileStream fs) { MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); byte[] md5byte = md5.ComputeHash(fs); string str = string.Empty; int i, j; foreach (byte b in md5byte) { i = Convert.ToInt32(b); j = i >> 4; str += (Convert.ToString(j, 16)); j = ((i << 4) & 0x00ff) >> 4; str += (Convert.ToString(j, 16)); } return str.ToUpper(); } #endregion //从一个目录生成一个CFC文件集合 public static void BuildCFCFileFromDirectory(string directory) { List<byte> bytes = new List<byte>(); foreach (string file in Directory.GetFiles(directory)) { if (!IsImageFile(file)) continue; bytes.AddRange(FaceBlock.FromImage(file).ToBytes()); } string fName = Path.Combine(directory, Path.GetDirectoryName(directory) + ".cfc"); FileStream fs = File.Create(fName); fs.Write(bytes.ToArray(), 0, bytes.Count); fs.Close(); } //判断是否为图像文件,方法比较简陋。 private static bool IsImageFile(string file) { List<string> validExt = new List<string>(new string[]{ ".bmp", ".jpg", ".jpeg", ".gif", ".png", }); return validExt.Contains(Path.GetExtension(file).ToLower()); } } 好,有了上面的方法,我们就可以调用了。 调用方法实在是有些简单。 FaceHelper.BuildCFCFileFromDirectory(Server.MapPath("~/img/")); 这样就OK了,现在去你的网站根目录下看看,有没有一个img.cfc的文件呢?再双击一下,是不是将img目录下的文件全部导入到QQ表情里了呢? enjoy coding! 本文原发:无垠IT教学网 http://www.cnblogs.com/skyover/archive/2006/10/03/520581.html 查看 http://www.qqread.com/qq/v340812.html |