WEB开发网
开发学院WEB开发ASP.NET 编程实现QQ表情文件CFC格式 阅读

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

 2006-10-05 17:14:52 来源:WEB开发网   
核心提示:背景:最近闲来无事,也应论坛某会员要求,编程实现QQ表情文件CFC格式,想做个QQ表情下载的站点,本来事情是很简单的,FaceHelper.BuildCFCFileFromDirectory(Server.MapPath("~/img/"));这样就OK了,现在去你的网站根目录下看看,写个小小的CR

背景:最近闲来无事,也应论坛某会员要求,想做个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

Tags:编程 实现 QQ

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接