现在的位置: 首页 > 综合 > 正文

编程实现QQ表情文件CFC格式

2013年10月01日 ⁄ 综合 ⁄ 共 5489字 ⁄ 字号 评论关闭
背景:最近闲来无事,也应论坛会员要求,想做个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 bytes = new List();
  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 bytes = new List();
  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 bytes = new List();
   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 validExt = new List(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!  

抱歉!评论已关闭.